apiTokens.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import {
  2. addErrorMessage,
  3. addLoadingMessage,
  4. addSuccessMessage,
  5. } from 'sentry/actionCreators/indicator';
  6. import AlertLink from 'sentry/components/alertLink';
  7. import {Button} from 'sentry/components/button';
  8. import EmptyMessage from 'sentry/components/emptyMessage';
  9. import ExternalLink from 'sentry/components/links/externalLink';
  10. import Panel from 'sentry/components/panels/panel';
  11. import PanelBody from 'sentry/components/panels/panelBody';
  12. import PanelHeader from 'sentry/components/panels/panelHeader';
  13. import {t, tct} from 'sentry/locale';
  14. import {InternalAppApiToken, Organization} from 'sentry/types';
  15. import withOrganization from 'sentry/utils/withOrganization';
  16. import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
  17. import ApiTokenRow from 'sentry/views/settings/account/apiTokenRow';
  18. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  19. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  20. type Props = {
  21. organization: Organization;
  22. } & DeprecatedAsyncView['props'];
  23. type State = {
  24. tokenList: InternalAppApiToken[] | null;
  25. } & DeprecatedAsyncView['state'];
  26. export class ApiTokens extends DeprecatedAsyncView<Props, State> {
  27. getTitle() {
  28. return t('User Auth Tokens');
  29. }
  30. getDefaultState() {
  31. return {
  32. ...super.getDefaultState(),
  33. tokenList: [],
  34. };
  35. }
  36. getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
  37. return [['tokenList', '/api-tokens/']];
  38. }
  39. handleRemoveToken = (token: InternalAppApiToken) => {
  40. addLoadingMessage();
  41. const oldTokenList = this.state.tokenList;
  42. this.setState(
  43. state => ({
  44. tokenList: state.tokenList?.filter(tk => tk.token !== token.token) ?? [],
  45. }),
  46. async () => {
  47. try {
  48. await this.api.requestPromise('/api-tokens/', {
  49. method: 'DELETE',
  50. data: {token: token.token},
  51. });
  52. addSuccessMessage(t('Removed token'));
  53. } catch (_err) {
  54. addErrorMessage(t('Unable to remove token. Please try again.'));
  55. this.setState({
  56. tokenList: oldTokenList,
  57. });
  58. }
  59. }
  60. );
  61. };
  62. renderBody() {
  63. const {organization} = this.props;
  64. const {tokenList} = this.state;
  65. const isEmpty = !Array.isArray(tokenList) || tokenList.length === 0;
  66. const action = (
  67. <Button
  68. priority="primary"
  69. size="sm"
  70. to="/settings/account/api/auth-tokens/new-token/"
  71. data-test-id="create-token"
  72. >
  73. {t('Create New Token')}
  74. </Button>
  75. );
  76. return (
  77. <div>
  78. <SettingsPageHeader title={this.getTitle()} action={action} />
  79. <AlertLink
  80. to={`/settings/${organization?.slug ?? ''}/developer-settings/new-internal`}
  81. >
  82. {t(
  83. "Auth Tokens are tied to the logged in user, meaning they'll stop working if the user leaves the organization! We suggest using internal integrations to create/manage tokens tied to the organization instead."
  84. )}
  85. </AlertLink>
  86. <TextBlock>
  87. {t(
  88. "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."
  89. )}
  90. </TextBlock>
  91. <TextBlock>
  92. {tct(
  93. 'For more information on how to use the web API, see our [link:documentation].',
  94. {
  95. link: <ExternalLink href="https://docs.sentry.io/api/" />,
  96. }
  97. )}
  98. </TextBlock>
  99. <Panel>
  100. <PanelHeader>{t('Auth Token')}</PanelHeader>
  101. <PanelBody>
  102. {isEmpty && (
  103. <EmptyMessage>
  104. {t("You haven't created any authentication tokens yet.")}
  105. </EmptyMessage>
  106. )}
  107. {tokenList?.map(token => (
  108. <ApiTokenRow
  109. key={token.token}
  110. token={token}
  111. onRemove={this.handleRemoveToken}
  112. />
  113. ))}
  114. </PanelBody>
  115. </Panel>
  116. </div>
  117. );
  118. }
  119. }
  120. export default withOrganization(ApiTokens);