11import { Fragment , useCallback , useMemo } from 'react' ;
22import styled from '@emotion/styled' ;
3+ import { useQuery , useQueryClient } from '@tanstack/react-query' ;
34import sortBy from 'lodash/sortBy' ;
45
56import { Button , LinkButton } from '@sentry/scraps/button' ;
@@ -20,15 +21,9 @@ import {t, tct} from 'sentry/locale';
2021import type { Integration , RepositoryProjectPathConfig } from 'sentry/types/integrations' ;
2122import { trackAnalytics } from 'sentry/utils/analytics' ;
2223import { useFetchAllPages } from 'sentry/utils/api/apiFetch' ;
23- import { getApiUrl } from 'sentry/utils/api/getApiUrl ' ;
24+ import { apiOptions , selectJsonWithHeaders } from 'sentry/utils/api/apiOptions ' ;
2425import { getIntegrationIcon } from 'sentry/utils/integrationUtil' ;
25- import {
26- useApiQuery ,
27- useInfiniteQuery ,
28- useMutation ,
29- useQueryClient ,
30- type ApiQueryKey ,
31- } from 'sentry/utils/queryClient' ;
26+ import { useInfiniteQuery , useMutation } from 'sentry/utils/queryClient' ;
3227import { organizationRepositoriesInfiniteOptions } from 'sentry/utils/repositories/repoQueryOptions' ;
3328import type { RequestError } from 'sentry/utils/requestError/requestError' ;
3429import { useRouteAnalyticsEventNames } from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames' ;
@@ -65,28 +60,33 @@ function getDocsLink(integration: Integration): string {
6560 return `https://docs.sentry.io/product/integrations/source-code-mgmt/${ docsKey } /#stack-trace-linking` ;
6661}
6762
68- function makePathConfigQueryKey ( {
63+ function codeMappingsApiOptions ( {
6964 orgSlug,
7065 integrationId,
7166 cursor,
7267} : {
73- integrationId : string ;
7468 orgSlug : string ;
7569 cursor ?: string | string [ ] | null ;
76- } ) : ApiQueryKey {
77- return [
78- getApiUrl ( '/organizations/$organizationIdOrSlug/code-mappings/' , {
70+ integrationId ?: string ;
71+ } ) {
72+ return apiOptions . as < RepositoryProjectPathConfig [ ] > ( ) (
73+ '/organizations/$organizationIdOrSlug/code-mappings/' ,
74+ {
7975 path : { organizationIdOrSlug : orgSlug } ,
80- } ) ,
81- { query : { integrationId, cursor} } ,
82- ] ;
76+ query : { integrationId, cursor} ,
77+ staleTime : 10_000 ,
78+ }
79+ ) ;
8380}
8481
85- function useDeletePathConfig ( ) {
82+ function useDeletePathConfig ( {
83+ queryKey,
84+ } : {
85+ queryKey : ReturnType < typeof codeMappingsApiOptions > [ 'queryKey' ] ;
86+ } ) {
8687 const api = useApi ( { persistInFlight : false } ) ;
8788 const organization = useOrganization ( ) ;
8889 const queryClient = useQueryClient ( ) ;
89- const location = useLocation ( ) ;
9090 return useMutation <
9191 RepositoryProjectPathConfig ,
9292 RequestError ,
@@ -102,15 +102,13 @@ function useDeletePathConfig() {
102102 } ,
103103 onMutate : pathConfig => {
104104 if ( pathConfig . integrationId ) {
105- queryClient . setQueryData < RepositoryProjectPathConfig [ ] > (
106- makePathConfigQueryKey ( {
107- orgSlug : organization . slug ,
108- integrationId : pathConfig . integrationId ,
109- cursor : location . query . cursor ,
110- } ) ,
111- ( data : RepositoryProjectPathConfig [ ] = [ ] ) => {
112- return data . filter ( config => config . id !== pathConfig . id ) ;
113- }
105+ queryClient . setQueryData ( queryKey , prevData =>
106+ prevData
107+ ? {
108+ ...prevData ,
109+ json : prevData . json . filter ( config => config . id !== pathConfig . id ) ,
110+ }
111+ : prevData
114112 ) ;
115113 }
116114 } ,
@@ -122,7 +120,9 @@ function useDeletePathConfig() {
122120 } ,
123121 onSettled : ( ) => {
124122 queryClient . invalidateQueries ( {
125- queryKey : [ `/organizations/${ organization . slug } /code-mappings/` ] ,
123+ queryKey : codeMappingsApiOptions ( {
124+ orgSlug : organization . slug ,
125+ } ) . queryKey ,
126126 } ) ;
127127 } ,
128128 } ) ;
@@ -144,19 +144,20 @@ export function IntegrationCodeMappings({integration}: {integration: Integration
144144 const location = useLocation ( ) ;
145145 const integrationId = integration . id ;
146146
147+ const pathConfigsQueryOptions = codeMappingsApiOptions ( {
148+ orgSlug : organization . slug ,
149+ integrationId,
150+ cursor : location . query . cursor ,
151+ } ) ;
152+
147153 const {
148- data : fetchedPathConfigs = [ ] ,
154+ data : pathConfigsResponse ,
149155 isPending : isPendingPathConfigs ,
150156 isError : isErrorPathConfigs ,
151- getResponseHeader : getPathConfigsResponseHeader ,
152- } = useApiQuery < RepositoryProjectPathConfig [ ] > (
153- makePathConfigQueryKey ( {
154- orgSlug : organization . slug ,
155- integrationId,
156- cursor : location . query . cursor ,
157- } ) ,
158- { staleTime : 10_000 }
159- ) ;
157+ } = useQuery ( {
158+ ...pathConfigsQueryOptions ,
159+ select : selectJsonWithHeaders ,
160+ } ) ;
160161
161162 const repositoriesQuery = useInfiniteQuery ( {
162163 ...organizationRepositoriesInfiniteOptions ( {
@@ -182,11 +183,11 @@ export function IntegrationCodeMappings({integration}: {integration: Integration
182183 ( ! ! hasNextReposPage && ! isErrorRepos ) ;
183184
184185 const pathConfigs = useMemo ( ( ) => {
185- return sortBy ( fetchedPathConfigs , [
186+ return sortBy ( pathConfigsResponse ?. json ?? [ ] , [
186187 ( { projectSlug} ) => projectSlug ,
187188 ( { id} ) => parseInt ( id , 10 ) ,
188189 ] ) ;
189- } , [ fetchedPathConfigs ] ) ;
190+ } , [ pathConfigsResponse ?. json ] ) ;
190191
191192 const repos = useMemo (
192193 ( ) => fetchedRepos . filter ( repo => repo . integrationId === integrationId ) ,
@@ -200,43 +201,40 @@ export function IntegrationCodeMappings({integration}: {integration: Integration
200201 [ projects ]
201202 ) ;
202203
203- const { mutate : deletePathConfig } = useDeletePathConfig ( ) ;
204+ const { mutate : deletePathConfig } = useDeletePathConfig ( {
205+ queryKey : pathConfigsQueryOptions . queryKey ,
206+ } ) ;
204207
205- const invalidateCodeMappings = useCallback ( ( ) => {
206- queryClient . invalidateQueries ( {
207- queryKey : [ `/organizations/${ organization . slug } /code-mappings/` ] ,
208+ const openCodeMappingModal = ( pathConfig ?: RepositoryProjectPathConfig ) => {
209+ trackAnalytics ( 'integrations.stacktrace_start_setup' , {
210+ setup_type : 'manual' ,
211+ view : 'integration_configuration_detail' ,
212+ provider : integration . provider . key ,
213+ organization,
208214 } ) ;
209- } , [ queryClient , organization . slug ] ) ;
210215
211- const openCodeMappingModal = useCallback (
212- ( pathConfig ?: RepositoryProjectPathConfig ) => {
213- trackAnalytics ( 'integrations.stacktrace_start_setup' , {
214- setup_type : 'manual' ,
215- view : 'integration_configuration_detail' ,
216- provider : integration . provider . key ,
217- organization,
218- } ) ;
219-
220- openModal (
221- modalProps => (
222- < RepositoryProjectPathConfigModal
223- { ...modalProps }
224- organization = { organization }
225- integration = { integration }
226- projects = { projects }
227- repos = { repos }
228- existingConfig = { pathConfig }
229- />
230- ) ,
231- {
232- onClose : ( ) => {
233- invalidateCodeMappings ( ) ;
234- } ,
235- }
236- ) ;
237- } ,
238- [ repos , projects , integration , organization , invalidateCodeMappings ]
239- ) ;
216+ openModal (
217+ modalProps => (
218+ < RepositoryProjectPathConfigModal
219+ { ...modalProps }
220+ organization = { organization }
221+ integration = { integration }
222+ projects = { projects }
223+ repos = { repos }
224+ existingConfig = { pathConfig }
225+ />
226+ ) ,
227+ {
228+ onClose : ( ) => {
229+ queryClient . invalidateQueries ( {
230+ queryKey : codeMappingsApiOptions ( {
231+ orgSlug : organization . slug ,
232+ } ) . queryKey ,
233+ } ) ;
234+ } ,
235+ }
236+ ) ;
237+ } ;
240238
241239 const isLoading = isPendingPathConfigs || isPendingRepos ;
242240
@@ -252,7 +250,7 @@ export function IntegrationCodeMappings({integration}: {integration: Integration
252250 return < LoadingError message = { t ( 'Error loading repositories' ) } /> ;
253251 }
254252
255- const pathConfigsPageLinks = getPathConfigsResponseHeader ?. ( ' Link' ) ;
253+ const pathConfigsPageLinks = pathConfigsResponse ?. headers . Link ;
256254 const docsLink = getDocsLink ( integration ) ;
257255
258256 return (
0 commit comments