diff --git a/static/app/utils/api/apiOptions.spec.tsx b/static/app/utils/api/apiOptions.spec.tsx index 0123732c5bba22..84060810722ab8 100644 --- a/static/app/utils/api/apiOptions.spec.tsx +++ b/static/app/utils/api/apiOptions.spec.tsx @@ -40,6 +40,35 @@ describe('apiOptions', () => { ]); }); + it('should not include options in queryKey when all values are undefined', () => { + const options = apiOptions.as()('/api-tokens/$tokenId/', { + staleTime: 0, + path: {tokenId: '123'}, + query: undefined, + method: undefined, + }); + + expect(options.queryKey).toEqual([ + {infinite: false, version: 'v2'}, + '/api-tokens/123/', + ]); + }); + + it('should strip undefined values from options in queryKey', () => { + const options = apiOptions.as()('/api-tokens/$tokenId/', { + staleTime: 0, + path: {tokenId: '123'}, + query: {cursor: 'abc'}, + method: undefined, + }); + + expect(options.queryKey).toEqual([ + {infinite: false, version: 'v2'}, + '/api-tokens/123/', + {query: {cursor: 'abc'}}, + ]); + }); + it('should stringify number path params', () => { const options = apiOptions.as()('/api-tokens/$tokenId/', { staleTime: 0, diff --git a/static/app/utils/api/apiOptions.ts b/static/app/utils/api/apiOptions.ts index 2271645782795f..2aa247aa406120 100644 --- a/static/app/utils/api/apiOptions.ts +++ b/static/app/utils/api/apiOptions.ts @@ -16,13 +16,17 @@ import {parseLinkHeader} from 'sentry/utils/parseLinkHeader'; type KnownApiUrls = KnownGetsentryApiUrls | KnownSentryApiUrls; -type Options = QueryKeyEndpointOptions & {staleTime: number}; +type Options = QueryKeyEndpointOptions & {staleTime: number | 'static'}; type PathParamOptions = ExtractPathParams extends never ? {path?: never} : {path: Record, string | number> | SkipToken}; +function stripUndefinedValues(obj: Record): Record { + return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)); +} + const selectJson = (data: ApiResponse) => data.json; export const selectJsonWithHeaders = ( @@ -43,11 +47,12 @@ function _apiOptions< : [Options & PathParamOptions] ) { const url = getApiUrl(path, ...([{path: pathParams}] as OptionalPathParams)); + const strippedOptions = stripUndefinedValues(options); return queryOptions({ queryKey: - Object.keys(options).length > 0 - ? ([{infinite: false, version: 'v2'}, url, options] as ApiQueryKey) + Object.keys(strippedOptions).length > 0 + ? ([{infinite: false, version: 'v2'}, url, strippedOptions] as ApiQueryKey) : ([{infinite: false, version: 'v2'}, url] as ApiQueryKey), queryFn: pathParams === skipToken ? skipToken : apiFetch, enabled: pathParams !== skipToken, @@ -77,11 +82,12 @@ function _apiOptionsInfinite< : [Options & PathParamOptions] ) { const url = getApiUrl(path, ...([{path: pathParams}] as OptionalPathParams)); + const strippedOptions = stripUndefinedValues(options); return infiniteQueryOptions({ queryKey: - Object.keys(options).length > 0 - ? ([{infinite: true, version: 'v2'}, url, options] as InfiniteApiQueryKey) + Object.keys(strippedOptions).length > 0 + ? ([{infinite: true, version: 'v2'}, url, strippedOptions] as InfiniteApiQueryKey) : ([{infinite: true, version: 'v2'}, url] as InfiniteApiQueryKey), queryFn: pathParams === skipToken ? skipToken : apiFetchInfinite, getPreviousPageParam: parsePageParam('previous'),