123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import {useCallback, useState} from 'react';
- import styled from '@emotion/styled';
- import {
- addErrorMessage,
- addLoadingMessage,
- addSuccessMessage,
- } from 'sentry/actionCreators/indicator';
- import FieldGroup from 'sentry/components/forms/fieldGroup';
- import TextField from 'sentry/components/forms/fields/textField';
- import Form from 'sentry/components/forms/form';
- import ExternalLink from 'sentry/components/links/externalLink';
- import Panel from 'sentry/components/panels/panel';
- import PanelBody from 'sentry/components/panels/panelBody';
- import PanelHeader from 'sentry/components/panels/panelHeader';
- import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
- import {t, tct} from 'sentry/locale';
- import type {Organization, OrgAuthToken} from 'sentry/types';
- import {browserHistory} from 'sentry/utils/browserHistory';
- import getDynamicText from 'sentry/utils/getDynamicText';
- import {handleXhrErrorResponse} from 'sentry/utils/handleXhrErrorResponse';
- import {useMutation, useQueryClient} from 'sentry/utils/queryClient';
- import type RequestError from 'sentry/utils/requestError/requestError';
- import useApi from 'sentry/utils/useApi';
- import {normalizeUrl} from 'sentry/utils/withDomainRequired';
- import withOrganization from 'sentry/utils/withOrganization';
- import NewTokenHandler from 'sentry/views/settings/components/newTokenHandler';
- import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
- import TextBlock from 'sentry/views/settings/components/text/textBlock';
- import {makeFetchOrgAuthTokensForOrgQueryKey} from 'sentry/views/settings/organizationAuthTokens';
- type CreateTokenQueryVariables = {
- name: string;
- };
- type OrgAuthTokenWithToken = OrgAuthToken & {token: string};
- type CreateOrgAuthTokensResponse = OrgAuthTokenWithToken;
- function AuthTokenCreateForm({
- organization,
- onCreatedToken,
- }: {
- onCreatedToken: (token: OrgAuthTokenWithToken) => void;
- organization: Organization;
- }) {
- const initialData = {
- name: '',
- };
- const api = useApi();
- const queryClient = useQueryClient();
- const handleGoBack = useCallback(() => {
- browserHistory.push(normalizeUrl(`/settings/${organization.slug}/auth-tokens/`));
- }, [organization.slug]);
- const {mutate: submitToken} = useMutation<
- CreateOrgAuthTokensResponse,
- RequestError,
- CreateTokenQueryVariables
- >({
- mutationFn: ({name}) => {
- addLoadingMessage();
- return api.requestPromise(`/organizations/${organization.slug}/org-auth-tokens/`, {
- method: 'POST',
- data: {
- name,
- },
- });
- },
- onSuccess: (token: OrgAuthTokenWithToken) => {
- addSuccessMessage(t('Created auth token.'));
- queryClient.invalidateQueries({
- queryKey: makeFetchOrgAuthTokensForOrgQueryKey({orgSlug: organization.slug}),
- });
- onCreatedToken(token);
- },
- onError: error => {
- const detail = error.responseJSON?.detail;
- const code = detail && typeof detail === 'object' ? detail.code : undefined;
- const message =
- code === 'missing_system_url_prefix'
- ? t(
- 'You have to configure `system.url-prefix` in your Sentry instance in order to generate tokens.'
- )
- : t('Failed to create a new auth token.');
- handleXhrErrorResponse(message, error);
- addErrorMessage(message);
- },
- });
- return (
- <Form
- apiMethod="POST"
- initialData={initialData}
- apiEndpoint={`/organizations/${organization.slug}/org-auth-tokens/`}
- onSubmit={({name}) => {
- submitToken({
- name,
- });
- }}
- onCancel={handleGoBack}
- submitLabel={t('Create Auth Token')}
- requireChanges
- >
- <TextField
- name="name"
- label={t('Name')}
- required
- help={t('A name to help you identify this token.')}
- />
- <FieldGroup
- label={t('Scopes')}
- help={t('Organization auth tokens currently have a limited set of scopes.')}
- >
- <div>
- <div>org:ci</div>
- <ScopeHelpText>{t('Source Map Upload, Release Creation')}</ScopeHelpText>
- </div>
- </FieldGroup>
- </Form>
- );
- }
- export function OrganizationAuthTokensNewAuthToken({
- organization,
- }: {
- organization: Organization;
- }) {
- const [newToken, setNewToken] = useState<OrgAuthTokenWithToken | null>(null);
- const handleGoBack = useCallback(() => {
- browserHistory.push(normalizeUrl(`/settings/${organization.slug}/auth-tokens/`));
- }, [organization.slug]);
- return (
- <div>
- <SentryDocumentTitle title={t('Create New Auth Token')} />
- <SettingsPageHeader title={t('Create New Auth Token')} />
- <TextBlock>
- {t(
- 'Organization Auth Tokens can be used in many places to interact with Sentry programatically. For example, they can be used for sentry-cli, bundler plugins or similar uses cases.'
- )}
- </TextBlock>
- <TextBlock>
- {tct(
- 'For more information on how to use the web API, see our [link:documentation].',
- {
- link: <ExternalLink href="https://docs.sentry.io/api/" />,
- }
- )}
- </TextBlock>
- <Panel>
- <PanelHeader>{t('Create New Auth Token')}</PanelHeader>
- <PanelBody>
- {newToken ? (
- <NewTokenHandler
- token={getDynamicText({value: newToken.token, fixed: 'ORG_AUTH_TOKEN'})}
- handleGoBack={handleGoBack}
- />
- ) : (
- <AuthTokenCreateForm
- organization={organization}
- onCreatedToken={setNewToken}
- />
- )}
- </PanelBody>
- </Panel>
- </div>
- );
- }
- export default withOrganization(OrganizationAuthTokensNewAuthToken);
- const ScopeHelpText = styled('div')`
- color: ${p => p.theme.gray300};
- `;
|