Skip to content
137 changes: 46 additions & 91 deletions static/app/views/automations/hooks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {queryOptions} from '@tanstack/react-query';

import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import {t, tn} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import type {Action, ActionHandler} from 'sentry/types/workflowEngine/actions';
import type {
Automation,
Expand All @@ -10,6 +13,7 @@ import type {
DataConditionHandler,
DataConditionHandlerGroupType,
} from 'sentry/types/workflowEngine/dataConditions';
import {apiOptions} from 'sentry/utils/api/apiOptions';
import {getApiUrl} from 'sentry/utils/api/getApiUrl';
import type {
ApiQueryKey,
Expand All @@ -26,73 +30,48 @@ import type {RequestError} from 'sentry/utils/requestError/requestError';
import {useApi} from 'sentry/utils/useApi';
import {useOrganization} from 'sentry/utils/useOrganization';

export const makeAutomationsQueryKey = ({
orgSlug,
query,
sortBy,
priorityDetector,
ids,
limit,
cursor,
projects,
detector,
}: {
orgSlug: string;
cursor?: string;
detector?: string[];
ids?: string[];
limit?: number;
priorityDetector?: string;
projects?: number[];
query?: string;
sortBy?: string;
}): ApiQueryKey => [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: orgSlug},
}),
{
query: {
export const automationsApiOptions = (
organization: Organization,
options?: {
cursor?: string;
Comment thread
TkDodo marked this conversation as resolved.
detector?: string[];
ids?: string[];
limit?: number;
priorityDetector?: string;
projects?: number[];
query?: string;
sortBy?: string;
}
) => {
const query = options
? {
query: options.query,
sortBy: options.sortBy,
priorityDetector: options.priorityDetector,
id: options.ids,
per_page: options.limit,
cursor: options.cursor,
project: options.projects,
detector: options.detector,
}
: undefined;

return queryOptions({
...apiOptions.as<Automation[]>()('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: organization.slug},
query,
sortBy,
priorityDetector,
id: ids,
per_page: limit,
cursor,
project: projects,
detector,
},
},
];
staleTime: 0,
}),
retry: false,
});
};
Comment thread
TkDodo marked this conversation as resolved.

const makeAutomationQueryKey = (orgSlug: string, automationId: string): ApiQueryKey => [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/$workflowId/', {
path: {organizationIdOrSlug: orgSlug, workflowId: automationId},
}),
];

interface UseAutomationsQueryOptions {
cursor?: string;
detector?: string[];
ids?: string[];
limit?: number;
priorityDetector?: string;
projects?: number[];
query?: string;
sortBy?: string;
}
export function useAutomationsQuery(
options: UseAutomationsQueryOptions = {},
queryOptions: Partial<UseApiQueryOptions<Automation[]>> = {}
) {
const {slug: orgSlug} = useOrganization();

return useApiQuery<Automation[]>(makeAutomationsQueryKey({orgSlug, ...options}), {
staleTime: 0,
retry: false,
...queryOptions,
});
}

export function useAutomationQuery(automationId: string) {
const {slug} = useOrganization();

Expand Down Expand Up @@ -129,7 +108,7 @@ interface UseAutomationFireHistoryQueryOptions {
}
export function useAutomationFireHistoryQuery(
options: UseAutomationFireHistoryQueryOptions,
queryOptions: Partial<UseApiQueryOptions<AutomationFireHistory[]>> = {}
useApiQueryOptions: Partial<UseApiQueryOptions<AutomationFireHistory[]>> = {}
) {
const {slug} = useOrganization();

Expand All @@ -138,7 +117,7 @@ export function useAutomationFireHistoryQuery(
{
staleTime: 5 * 60 * 1000, // 5 minutes
retry: false,
...queryOptions,
...useApiQueryOptions,
}
);
}
Expand Down Expand Up @@ -194,11 +173,7 @@ export function useCreateAutomation() {
),
onSuccess: _ => {
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
},
onError: _ => {
Expand All @@ -224,11 +199,7 @@ export function useDeleteAutomationMutation() {
),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
addSuccessMessage(t('Alert deleted'));
},
Expand Down Expand Up @@ -266,11 +237,7 @@ export function useDeleteAutomationsMutation() {
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
addSuccessMessage(t('Alerts deleted'));
},
Expand Down Expand Up @@ -313,11 +280,7 @@ export function useUpdateAutomation() {
);
// Invalidate list query
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
},
onError: _ => {
Expand Down Expand Up @@ -355,11 +318,7 @@ export function useUpdateAutomationsMutation() {
},
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
addSuccessMessage(variables.enabled ? t('Alerts enabled') : t('Alerts disabled'));
},
Expand Down Expand Up @@ -396,11 +355,7 @@ export function useSendTestNotification(
...options,
onSuccess: (data, variables, onMutateResult, context) => {
queryClient.invalidateQueries({
queryKey: [
getApiUrl('/organizations/$organizationIdOrSlug/workflows/', {
path: {organizationIdOrSlug: org.slug},
}),
],
queryKey: automationsApiOptions(org).queryKey,
});
addSuccessMessage(
tn('Notification fired!', 'Notifications sent!', variables.length)
Expand Down
36 changes: 16 additions & 20 deletions static/app/views/automations/list.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {useCallback} from 'react';
import {useQuery} from '@tanstack/react-query';

import {LinkButton} from '@sentry/scraps/button';
import {Flex} from '@sentry/scraps/layout';
Expand All @@ -10,6 +11,7 @@ import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle';
import {WorkflowEngineListLayout as ListLayout} from 'sentry/components/workflowEngine/layout/list';
import {IconAdd} from 'sentry/icons';
import {t} from 'sentry/locale';
import {selectJsonWithHeaders} from 'sentry/utils/api/apiOptions';
import {parseLinkHeader} from 'sentry/utils/parseLinkHeader';
import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
import {decodeScalar, decodeSorts} from 'sentry/utils/queryString';
Expand All @@ -21,11 +23,12 @@ import {AutomationFeedbackButton} from 'sentry/views/automations/components/auto
import {AutomationListTable} from 'sentry/views/automations/components/automationListTable';
import {AutomationSearch} from 'sentry/views/automations/components/automationListTable/search';
import {AUTOMATION_LIST_PAGE_LIMIT} from 'sentry/views/automations/constants';
import {useAutomationsQuery} from 'sentry/views/automations/hooks';
import {automationsApiOptions} from 'sentry/views/automations/hooks';
import {makeAutomationCreatePathname} from 'sentry/views/automations/pathnames';
import {AlertsRedirectNotice} from 'sentry/views/detectors/list/common/alertsRedirectNotice';

export default function AutomationsList() {
const organization = useOrganization();
const location = useLocation();
const navigate = useNavigate();
const {selection, isReady} = usePageFilters();
Expand All @@ -43,30 +46,23 @@ export default function AutomationsList() {
});
const sort = sorts[0] ?? {kind: 'desc', field: 'lastTriggered'};

const {
data: automations,
isLoading,
isError,
isSuccess,
getResponseHeader,
} = useAutomationsQuery(
{
cursor,
const {data, isLoading, isError, isSuccess} = useQuery({
...automationsApiOptions(organization, {
query,
sortBy: sort ? `${sort?.kind === 'asc' ? '' : '-'}${sort?.field}` : undefined,
projects: selection.projects,
limit: AUTOMATION_LIST_PAGE_LIMIT,
},
{enabled: isReady}
);
cursor,
}),
select: selectJsonWithHeaders,
enabled: isReady,
});

const hits = getResponseHeader?.('X-Hits') || '';
const hitsInt = hits ? parseInt(hits, 10) || 0 : 0;
const automations = data?.json;
const hits = data?.headers['X-Hits'] ?? 0;
// If maxHits is not set, we assume there is no max
const maxHits = getResponseHeader?.('X-Max-Hits') || '';
const maxHitsInt = maxHits ? parseInt(maxHits, 10) || Infinity : Infinity;

const pageLinks = getResponseHeader?.('Link');
const maxHits = data?.headers['X-Max-Hits'] ?? Infinity;
const pageLinks = data?.headers.Link;

const allResultsVisible = useCallback(() => {
if (!pageLinks) {
Expand Down Expand Up @@ -102,7 +98,7 @@ export default function AutomationsList() {
isError={isError}
isSuccess={isSuccess}
sort={sort}
queryCount={hitsInt > maxHitsInt ? `${maxHits}+` : hits}
queryCount={hits > maxHits ? `${maxHits}+` : `${hits}`}
allResultsVisible={allResultsVisible()}
/>
</VisuallyCompleteWithData>
Comment thread
TkDodo marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {DrawerHeader} from 'sentry/components/globalDrawer/components';
import {DetailSection} from 'sentry/components/workflowEngine/ui/detailSection';
import {t} from 'sentry/locale';
import type {Automation} from 'sentry/types/workflowEngine/automations';
import {getApiQueryData, setApiQueryData, useQueryClient} from 'sentry/utils/queryClient';
import {useQueryClient} from 'sentry/utils/queryClient';
import {useOrganization} from 'sentry/utils/useOrganization';
import {AutomationSearch} from 'sentry/views/automations/components/automationListTable/search';
import {makeAutomationsQueryKey} from 'sentry/views/automations/hooks';
import {automationsApiOptions} from 'sentry/views/automations/hooks';
import {ConnectedAutomationsList} from 'sentry/views/detectors/components/connectedAutomationList';

function ConnectedAutomations({
Expand Down Expand Up @@ -81,13 +81,11 @@ export function ConnectAutomationsDrawer({

const toggleConnected = ({automation}: {automation: Automation}) => {
const oldAutomationsData =
getApiQueryData<Automation[]>(
queryClient,
makeAutomationsQueryKey({
orgSlug: organization.slug,
queryClient.getQueryData(
automationsApiOptions(organization, {
ids: localWorkflowIds,
})
) ?? [];
}).queryKey
)?.json ?? [];

const newAutomations = (
oldAutomationsData.some(a => a.id === automation.id)
Expand All @@ -96,13 +94,9 @@ export function ConnectAutomationsDrawer({
).sort((a, b) => a.id.localeCompare(b.id));
const newWorkflowIds = newAutomations.map(a => a.id);

setApiQueryData<Automation[]>(
queryClient,
makeAutomationsQueryKey({
orgSlug: organization.slug,
ids: newWorkflowIds,
}),
newAutomations
queryClient.setQueryData(
automationsApiOptions(organization, {ids: newWorkflowIds}).queryKey,
old => ({headers: old?.headers ?? {}, json: newAutomations})
);

setLocalWorkflowIds(newWorkflowIds);
Expand Down
Loading
Loading