diff --git a/static/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx b/static/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx index 3fa2d2c3eaaf..59ab431db0f9 100644 --- a/static/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx +++ b/static/app/views/settings/organizationDeveloperSettings/sentryApplicationDetails.tsx @@ -221,9 +221,6 @@ function getSchemaFieldValue(schema: SentryApp['schema'] | null | undefined) { } export default function SentryApplicationDetails() { - const {openModal} = useModal(); - - const navigate = useNavigate(); const location = useLocation(); const {appSlug} = useParams<{appSlug: string}>(); const organization = useOrganization(); @@ -231,18 +228,15 @@ export default function SentryApplicationDetails() { const hasPageFrame = useHasPageFrameFeature(); const queryClient = useQueryClient(); - const isEditingApp = !!appSlug; + const isInternalRoute = location.pathname.endsWith('new-internal/'); - const SENTRY_APP_API_TOKENS_QUERY_KEY = makeSentryAppApiTokensQueryKey(appSlug); - - const sentryAppQueryOptions = sentryAppApiOptions({ - appSlug: isEditingApp ? appSlug : null, - }); + const sentryAppQueryOptions = sentryAppApiOptions({appSlug: appSlug ?? null}); const { data: app, - isPending, + isLoading, isError, + isPlaceholderData, refetch, } = useQuery({ ...sentryAppQueryOptions, @@ -260,13 +254,60 @@ export default function SentryApplicationDetails() { return found ? {json: found, headers: {}} : undefined; }, }); + const {data: tokens = []} = useApiQuery( - SENTRY_APP_API_TOKENS_QUERY_KEY, - { - staleTime: 30000, - enabled: isEditingApp, - } + makeSentryAppApiTokensQueryKey(appSlug ?? ''), + {staleTime: 30_000, enabled: !!appSlug} ); + + const isInternal = app ? app.status === 'internal' : isInternalRoute; + const headerTitle = tct('[action] [type] Integration', { + action: app ? 'Edit' : 'Create', + type: isInternal ? 'Internal' : 'Public', + }); + + return ( +
+ {hasPageFrame ? ( + + ) : ( + + )} + + {isLoading || isPlaceholderData ? ( + + ) : isError ? ( + + ) : ( + + )} +
+ ); +} + +function SentryApplicationForm({ + app, + appSlug, + tokens, + isInternal, +}: { + app: SentryApp | undefined; + appSlug: string | undefined; + isInternal: boolean; + tokens: InternalAppApiToken[]; +}) { + const {openModal} = useModal(); + const navigate = useNavigate(); + const organization = useOrganization(); + const queryClient = useQueryClient(); + + const sentryAppQueryOptions = sentryAppApiOptions({appSlug: appSlug ?? null}); + const [newTokens, setNewTokens] = useState([]); const [scopeErrors, setScopeErrors] = useState({permissions: {}}); @@ -325,23 +366,10 @@ export default function SentryApplicationDetails() { return organization.access.includes('org:write'); }; - const isInternal = () => { - if (app) { - return app.status === 'internal'; - } - return location.pathname.endsWith('new-internal/'); - }; - const showAuthInfo = () => !(app?.clientSecret?.[0] === '*'); - const headerTitle = () => { - const action = app ? 'Edit' : 'Create'; - const type = isInternal() ? 'Internal' : 'Public'; - return tct('[action] [type] Integration', {action, type}); - }; - const handleSubmitSuccess = (data: SentryApp) => { - const type = isInternal() ? 'internal' : 'public'; + const type = isInternal ? 'internal' : 'public'; const baseUrl = `/settings/${organization.slug}/developer-settings/`; const url = app ? `${baseUrl}?type=${type}` : `${baseUrl}${data.slug}/`; @@ -359,7 +387,7 @@ export default function SentryApplicationDetails() { } ); - refetch(); + queryClient.invalidateQueries({queryKey: sentryAppQueryOptions.queryKey}); } else { addSuccessMessage(t('%s successfully created.', data.name)); } @@ -382,7 +410,11 @@ export default function SentryApplicationDetails() { const handleFinishNewToken = (newToken: NewInternalAppApiToken) => { const updatedNewTokens = newTokens.filter(token => token.id !== newToken.id); const updatedTokens = tokens.concat(newToken); - setApiQueryData(queryClient, SENTRY_APP_API_TOKENS_QUERY_KEY, updatedTokens); + setApiQueryData( + queryClient, + makeSentryAppApiTokensQueryKey(appSlug ?? ''), + updatedTokens + ); setNewTokens(updatedNewTokens); }; @@ -393,7 +425,11 @@ export default function SentryApplicationDetails() { const updatedTokens = tokens.filter(tok => tok.id !== token.id); await removeTokenMutation.mutateAsync({sentryAppSlug: app.slug, tokenId: token.id}); - setApiQueryData(queryClient, SENTRY_APP_API_TOKENS_QUERY_KEY, updatedTokens); + setApiQueryData( + queryClient, + makeSentryAppApiTokensQueryKey(appSlug ?? ''), + updatedTokens + ); }; const renderTokens = () => { @@ -472,7 +508,7 @@ export default function SentryApplicationDetails() { model={app} onSave={addAvatar} title={isColor ? t('Logo') : t('Small Icon')} - help={styleProps.help.concat(isInternal() ? '' : t(' Required for publishing.'))} + help={styleProps.help.concat(isInternal ? '' : t(' Required for publishing.'))} defaultChoice={{ label: styleProps.label, description: styleProps.description, @@ -486,13 +522,13 @@ export default function SentryApplicationDetails() { author: app?.author ?? '', webhookUrl: app?.webhookUrl ?? '', redirectUrl: app?.redirectUrl ?? '', - verifyInstall: isInternal() ? false : (app?.verifyInstall ?? true), + verifyInstall: isInternal ? false : (app?.verifyInstall ?? true), isAlertable: app?.isAlertable ?? false, schema: getSchemaFieldValue(app?.schema), overview: app?.overview ?? '', allowedOrigins: convertMultilineFieldValue(app?.allowedOrigins ?? []), organization: organization.slug, - isInternal: isInternal(), + isInternal, scopes: app ? [...app.scopes] : [], events: app ? normalize(app.events) : [], }; @@ -591,321 +627,295 @@ export default function SentryApplicationDetails() { }); return ( -
- {hasPageFrame ? ( - - ) : ( - - )} - - {isEditingApp && isPending ? ( - - ) : isEditingApp && isError ? ( - - ) : ( - - - - {field => ( - - - - )} - - - {!isInternal() && ( - - {field => ( - - - + + + + {field => ( + + + + )} + + + {!isInternal && ( + + {field => ( + + required + > + + )} - - { - const isAlertable = fieldApi.form.getFieldValue('isAlertable'); - if (isInternal() && !value && isAlertable) { - fieldApi.form.setFieldValue('isAlertable', false); - } - }, - }} - > - {field => ( - - ), - } - )} - required={!isInternal()} - > - - + + )} + + { + const isAlertable = fieldApi.form.getFieldValue('isAlertable'); + if (isInternal && !value && isAlertable) { + fieldApi.form.setFieldValue('isAlertable', false); + } + }, + }} + > + {field => ( + + ), + } )} - - - {!isInternal() && ( - - {field => ( - - - - )} - + required={!isInternal} + > + + + )} + + + {!isInternal && ( + + {field => ( + + + )} - - {!isInternal() && ( - - {field => ( - - - + + )} + + {!isInternal && ( + + {field => ( + + > + + )} - - - {field => ( - - ), - } - )} - > - isInternal() && !state.values.webhookUrl} - > - {webhookDisabled => ( - - )} - - - )} - - - - {field => ( - - ), - } - )} - > - - + + )} + + + {field => ( + + ), + } )} - - - - {field => ( - - - - )} - - - - {field => ( - - + isInternal && !state.values.webhookUrl}> + {webhookDisabled => ( + - + )} + + + )} + + + + {field => ( + + ), + } )} - - - - {getAvatarChooser(true)} - {getAvatarChooser(false)} - - isInternal() && !state.values.webhookUrl}> - {webhookDisabled => ( - form.setFieldValue('scopes', scopes)} - onEventsChange={events => form.setFieldValue('events', events)} + > + - )} - - - {app?.status === 'internal' && ( - - - , - ]} - isEmpty={tokens.length === 0} - emptyMessage={t("You haven't created any authentication tokens yet.")} + + )} + + + + {field => ( + - {renderTokens()} - + + )} + - {app && ( - - {t('Credentials')} - - {app.status !== 'internal' && ( - - {({id}: {id: string}) => ( - {app.clientId ?? ''} - )} - + + {field => ( + + + + )} + + + + {getAvatarChooser(true)} + {getAvatarChooser(false)} + + isInternal && !state.values.webhookUrl}> + {webhookDisabled => ( + form.setFieldValue('scopes', scopes)} + onEventsChange={events => form.setFieldValue('events', events)} + /> + )} + + + {app?.status === 'internal' && ( + + + , + ]} + isEmpty={tokens.length === 0} + emptyMessage={t("You haven't created any authentication tokens yet.")} + > + {renderTokens()} + + )} + + {app && ( + + {t('Credentials')} + + {app.status !== 'internal' && ( + + {({id}: {id: string}) => ( + {app.clientId ?? ''} )} - + )} + - {({id}: {id: string}) => - app.clientSecret ? ( - + {({id}: {id: string}) => + app.clientSecret ? ( + + {app.clientSecret} + + ) : ( + + {t('hidden')} + {hasTokenAccess() ? ( + - {app.clientSecret} - - ) : ( - - {t('hidden')} - {hasTokenAccess() ? ( - - - - ) : undefined} - - ) - } - - - - )} - - - {t('Save Changes')} - - + + + ) : undefined} + + ) + } + + + )} -
+ + + {t('Save Changes')} + + ); }