Browse Source

fix(scm): don't fetch repositories with empty query (#78748)

Cathy Teng 5 months ago
parent
commit
4b80550af0

+ 23 - 0
static/app/views/settings/organizationIntegrations/integrationRepos.spec.tsx

@@ -36,12 +36,31 @@ describe('IntegrationRepos', function () {
       });
 
       render(<IntegrationRepos integration={integration} />);
+      // we only attempt to fetch repositories upon typing
+      await userEvent.click(screen.getByText('Add Repository'));
+      await userEvent.type(screen.getByRole('textbox'), 'asdf');
+
       expect(
         await screen.findByText(
           /We were unable to fetch repositories for this integration/
         )
       ).toBeInTheDocument();
     });
+
+    it('does not fetch repositories with empty query', async function () {
+      MockApiClient.addMockResponse({
+        url: `/organizations/${org.slug}/repos/`,
+        method: 'GET',
+        body: [],
+      });
+
+      render(<IntegrationRepos integration={integration} />);
+      await userEvent.click(screen.getByText('Add Repository'));
+
+      expect(
+        await screen.findByText(/Please enter a repository name/)
+      ).toBeInTheDocument();
+    });
   });
 
   describe('Adding repositories', function () {
@@ -65,6 +84,7 @@ describe('IntegrationRepos', function () {
 
       render(<IntegrationRepos integration={integration} />);
       await userEvent.click(screen.getByText('Add Repository'));
+      await userEvent.type(screen.getByRole('textbox'), 'repo-name');
       await userEvent.click(screen.getByText('repo-name'));
 
       expect(addRepo).toHaveBeenCalledWith(
@@ -107,6 +127,7 @@ describe('IntegrationRepos', function () {
 
       render(<IntegrationRepos integration={integration} />);
       await userEvent.click(screen.getByText('Add Repository'));
+      await userEvent.type(screen.getByRole('textbox'), 'sentry-repo');
       await userEvent.click(screen.getByText('sentry-repo'));
 
       expect(addRepo).toHaveBeenCalled();
@@ -163,6 +184,7 @@ describe('IntegrationRepos', function () {
       render(<IntegrationRepos integration={integration} />);
 
       await userEvent.click(screen.getByText('Add Repository'));
+      await userEvent.type(screen.getByRole('textbox'), 'repo-name');
       await userEvent.click(screen.getByText('repo-name'));
 
       expect(updateRepo).toHaveBeenCalledWith(
@@ -195,6 +217,7 @@ describe('IntegrationRepos', function () {
       render(<IntegrationRepos integration={integration} />);
 
       await userEvent.click(screen.getByText('Add Repository'));
+      await userEvent.type(screen.getByRole('textbox'), 'repo-name');
       await userEvent.click(screen.getByText('repo-name'));
 
       expect(getItems).toHaveBeenCalled();

+ 13 - 14
static/app/views/settings/organizationIntegrations/integrationReposAddRepository.tsx

@@ -1,4 +1,4 @@
-import {useCallback, useEffect, useMemo, useState} from 'react';
+import {useCallback, useMemo, useState} from 'react';
 import styled from '@emotion/styled';
 import debounce from 'lodash/debounce';
 
@@ -25,7 +25,6 @@ interface IntegrationReposAddRepositoryProps {
 
 interface IntegrationRepoSearchResult {
   repos: IntegrationRepository[];
-  searchable: boolean;
 }
 
 export function IntegrationReposAddRepository({
@@ -36,11 +35,10 @@ export function IntegrationReposAddRepository({
 }: IntegrationReposAddRepositoryProps) {
   const api = useApi({persistInFlight: true});
   const organization = useOrganization();
-  const [dropdownBusy, setDropdownBusy] = useState(true);
+  const [dropdownBusy, setDropdownBusy] = useState(false);
   const [adding, setAdding] = useState(false);
   const [searchResult, setSearchResult] = useState<IntegrationRepoSearchResult>({
     repos: [],
-    searchable: false,
   });
 
   const searchRepositoriesRequest = useCallback(
@@ -59,11 +57,6 @@ export function IntegrationReposAddRepository({
     [api, integration, organization, onSearchError]
   );
 
-  useEffect(() => {
-    // Load the repositories before the dropdown is opened
-    searchRepositoriesRequest();
-  }, [searchRepositoriesRequest]);
-
   const debouncedSearchRepositoriesRequest = useMemo(
     () => debounce(query => searchRepositoriesRequest(query), 200),
     [searchRepositoriesRequest]
@@ -71,9 +64,15 @@ export function IntegrationReposAddRepository({
 
   const handleSearchRepositories = useCallback(
     (e?: React.ChangeEvent<HTMLInputElement>) => {
-      setDropdownBusy(true);
-      onSearchError(null);
-      debouncedSearchRepositoriesRequest(e?.target.value);
+      if (e?.target.value) {
+        setDropdownBusy(true);
+        onSearchError(null);
+        debouncedSearchRepositoriesRequest(e?.target.value);
+      } else {
+        setDropdownBusy(false);
+        onSearchError(null);
+        setSearchResult({repos: []});
+      }
     },
     [debouncedSearchRepositoriesRequest, onSearchError]
   );
@@ -144,8 +143,8 @@ export function IntegrationReposAddRepository({
       <DropdownAutoComplete
         items={dropdownItems}
         onSelect={addRepo}
-        onChange={searchResult.searchable ? handleSearchRepositories : undefined}
-        emptyMessage={t('No repositories available')}
+        onChange={handleSearchRepositories}
+        emptyMessage={t('Please enter a repository name')}
         noResultsMessage={t('No repositories found')}
         searchPlaceholder={t('Search Repositories')}
         busy={dropdownBusy}