]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19728 Unlock project issues page while reindexing issues
authorAmbroise C <ambroise.christea@sonarsource.com>
Tue, 18 Jul 2023 15:23:24 +0000 (17:23 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 19 Jul 2023 20:03:05 +0000 (20:03 +0000)
server/sonar-web/src/main/js/api/issues.ts
server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx
server/sonar-web/src/main/js/apps/issues/test-utils.tsx
server/sonar-web/src/main/js/apps/issues/utils.ts
server/sonar-web/src/main/js/helpers/branch-like.ts
server/sonar-web/src/main/js/types/issues.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index af5ee866a1cbd3f60764a563503f8b17fc081a1a..23e47c35d88ae1fb4a800f734e4ed9fc8d104c47 100644 (file)
@@ -28,7 +28,7 @@ import {
   postJSON,
   RequestData,
 } from '../helpers/request';
-import { IssueResponse, RawIssuesResponse } from '../types/issues';
+import { IssueResponse, ListIssuesResponse, RawIssuesResponse } from '../types/issues';
 import { Dict, FacetValue, IssueChangelog, SnippetsByComponent, SourceLine } from '../types/types';
 
 type FacetName =
@@ -55,6 +55,10 @@ export function searchIssues(query: RequestData): Promise<RawIssuesResponse> {
   return getJSON('/api/issues/search', query).catch(throwGlobalError);
 }
 
+export function listIssues(query: RequestData): Promise<ListIssuesResponse> {
+  return getJSON('/api/issues/list', query).catch(throwGlobalError);
+}
+
 export function getFacets(
   query: RequestData,
   facets: FacetName[]
index 080d3b941e9f0cf226f0abeb097b94adc1fb72a8..efaf2bd2c709c40ac5fc7981d3f8c9d725e810f2 100644 (file)
@@ -32,6 +32,7 @@ import {
   IssueStatus,
   IssueTransition,
   IssueType,
+  ListIssuesResponse,
   RawFacet,
   RawIssue,
   RawIssuesResponse,
@@ -49,6 +50,7 @@ import {
   editIssueComment,
   getIssueChangelog,
   getIssueFlowSnippets,
+  listIssues,
   searchIssueAuthors,
   searchIssueTags,
   searchIssues,
@@ -116,6 +118,7 @@ export default class IssuesServiceMock {
     jest.mocked(getIssueChangelog).mockImplementation(this.handleGetIssueChangelog);
     jest.mocked(getIssueFlowSnippets).mockImplementation(this.handleGetIssueFlowSnippets);
     jest.mocked(getRuleDetails).mockImplementation(this.handleGetRuleDetails);
+    jest.mocked(listIssues).mockImplementation(this.handleListIssues);
     jest.mocked(searchIssueAuthors).mockImplementation(this.handleSearchIssueAuthors);
     jest.mocked(searchIssues).mockImplementation(this.handleSearchIssues);
     jest.mocked(searchIssueTags).mockImplementation(this.handleSearchIssueTags);
@@ -379,6 +382,32 @@ export default class IssuesServiceMock {
     });
   };
 
+  handleListIssues = (query: RequestData): Promise<ListIssuesResponse> => {
+    const filteredList = this.list
+      .filter((item) => !query.types || query.types.split(',').includes(item.issue.type))
+      .filter(
+        (item) =>
+          !query.inNewCodePeriod || new Date(item.issue.creationDate) > new Date('2023-01-10')
+      );
+
+    // Splice list items according to paging using a fixed page size
+    const pageIndex = query.p || 1;
+    const pageSize = 7;
+    const listItems = filteredList.slice((pageIndex - 1) * pageSize, pageIndex * pageSize);
+
+    // Generate response
+    return this.reply({
+      components: generateReferenceComponentsForIssues(filteredList),
+      issues: listItems.map((line) => line.issue),
+      paging: mockPaging({
+        pageIndex,
+        pageSize,
+        total: filteredList.length,
+      }),
+      rules: this.rulesList,
+    });
+  };
+
   handleSearchIssues = (query: RequestData): Promise<RawIssuesResponse> => {
     const facets = this.mockFacetDetailResponse(query);
 
index 7d726e9492dd1a71013e5ebe8db1ff3d3386e919..b70a6cdc61cec991b324baa5ff6c3d104886c5cd 100644 (file)
@@ -957,3 +957,30 @@ describe('Activity', () => {
     ).toBeInTheDocument();
   });
 });
+
+describe('issues app when reindexing', () => {
+  it('should display only some facets while reindexing is in progress', async () => {
+    issuesHandler.setIsAdmin(true);
+    renderProjectIssuesApp(undefined, { needIssueSync: true });
+
+    // Enabled facets
+    expect(ui.inNewCodeFilter.get()).toBeInTheDocument();
+    expect(ui.typeFacet.get()).toBeInTheDocument();
+
+    // Disabled facets
+    expect(await ui.assigneeFacet.query()).not.toBeInTheDocument();
+    expect(await ui.authorFacet.query()).not.toBeInTheDocument();
+    expect(await ui.codeVariantsFacet.query()).not.toBeInTheDocument();
+    expect(await ui.creationDateFacet.query()).not.toBeInTheDocument();
+    expect(await ui.languageFacet.query()).not.toBeInTheDocument();
+    expect(await ui.projectFacet.query()).not.toBeInTheDocument();
+    expect(await ui.resolutionFacet.query()).not.toBeInTheDocument();
+    expect(await ui.ruleFacet.query()).not.toBeInTheDocument();
+    expect(await ui.scopeFacet.query()).not.toBeInTheDocument();
+    expect(await ui.statusFacet.query()).not.toBeInTheDocument();
+    expect(await ui.tagFacet.query()).not.toBeInTheDocument();
+
+    // Indexation message
+    expect(screen.getByText(/indexation\.filters_unavailable/)).toBeInTheDocument();
+  });
+});
index 8e06b68f8d90f3734e99e5fdda9e794a40a8113b..6e0262b31a3d42be8850c5a3e44069016a1933b9 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { BasicSeparator } from 'design-system';
+import { BasicSeparator, FlagMessage, Link } from 'design-system';
 import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
 import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
 import { isBranch, isPullRequest } from '../../../helpers/branch-like';
+import { translate } from '../../../helpers/l10n';
 import { AppState } from '../../../types/appstate';
 import { BranchLike } from '../../../types/branch-like';
 import {
@@ -167,6 +169,8 @@ export class SidebarClass extends React.PureComponent<Props> {
     const displayPeriodFilter = component !== undefined && !isPortfolioLike(component.qualifier);
     const displayProjectsFacet = !component || isView(component.qualifier);
 
+    const needIssueSync = component?.needIssueSync;
+
     return (
       <>
         {displayPeriodFilter && (
@@ -178,6 +182,7 @@ export class SidebarClass extends React.PureComponent<Props> {
 
         <TypeFacet
           fetching={this.props.loadingFacets.types === true}
+          needIssueSync={needIssueSync}
           onChange={this.props.onFilterChange}
           onToggle={this.props.onFacetToggle}
           open={!!openFacets.types}
@@ -185,191 +190,220 @@ export class SidebarClass extends React.PureComponent<Props> {
           types={query.types}
         />
 
-        <BasicSeparator className="sw-my-4" />
-
-        <SeverityFacet
-          fetching={this.props.loadingFacets.severities === true}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.severities}
-          severities={query.severities}
-          stats={facets.severities}
-        />
-
-        <BasicSeparator className="sw-my-4" />
-
-        <ScopeFacet
-          fetching={this.props.loadingFacets.scopes === true}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.scopes}
-          stats={facets.scopes}
-          scopes={query.scopes}
-        />
-
-        <BasicSeparator className="sw-my-4" />
-
-        <ResolutionFacet
-          fetching={this.props.loadingFacets.resolutions === true}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.resolutions}
-          resolutions={query.resolutions}
-          resolved={query.resolved}
-          stats={facets.resolutions}
-        />
-
-        <BasicSeparator className="sw-my-4" />
+        {!needIssueSync && (
+          <>
+            <BasicSeparator className="sw-my-4" />
 
-        <StatusFacet
-          fetching={this.props.loadingFacets.statuses === true}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.statuses}
-          stats={facets.statuses}
-          statuses={query.statuses}
-        />
+            <SeverityFacet
+              fetching={this.props.loadingFacets.severities === true}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.severities}
+              severities={query.severities}
+              stats={facets.severities}
+            />
 
-        <BasicSeparator className="sw-my-4" />
+            <BasicSeparator className="sw-my-4" />
 
-        <StandardFacet
-          cwe={query.cwe}
-          cweOpen={!!openFacets.cwe}
-          cweStats={facets.cwe}
-          fetchingCwe={this.props.loadingFacets.cwe === true}
-          fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true}
-          fetchingOwaspTop10-2021={this.props.loadingFacets['owaspTop10-2021'] === true}
-          fetchingSonarSourceSecurity={this.props.loadingFacets.sonarsourceSecurity === true}
-          loadSearchResultCount={this.props.loadSearchResultCount}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.standards}
-          owaspTop10={query.owaspTop10}
-          owaspTop10Open={!!openFacets.owaspTop10}
-          owaspTop10Stats={facets.owaspTop10}
-          owaspTop10-2021={query['owaspTop10-2021']}
-          owaspTop10-2021Open={!!openFacets['owaspTop10-2021']}
-          owaspTop10-2021Stats={facets['owaspTop10-2021']}
-          query={query}
-          sonarsourceSecurity={query.sonarsourceSecurity}
-          sonarsourceSecurityOpen={!!openFacets.sonarsourceSecurity}
-          sonarsourceSecurityStats={facets.sonarsourceSecurity}
-        />
+            <ScopeFacet
+              fetching={this.props.loadingFacets.scopes === true}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.scopes}
+              stats={facets.scopes}
+              scopes={query.scopes}
+            />
 
-        <BasicSeparator className="sw-my-4" />
+            <BasicSeparator className="sw-my-4" />
 
-        <CreationDateFacet
-          component={component}
-          createdAfter={query.createdAfter}
-          createdAfterIncludesTime={createdAfterIncludesTime}
-          createdAt={query.createdAt}
-          createdBefore={query.createdBefore}
-          createdInLast={query.createdInLast}
-          fetching={this.props.loadingFacets.createdAt === true}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.createdAt}
-          inNewCodePeriod={query.inNewCodePeriod}
-          stats={facets.createdAt}
-        />
+            <ResolutionFacet
+              fetching={this.props.loadingFacets.resolutions === true}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.resolutions}
+              resolutions={query.resolutions}
+              resolved={query.resolved}
+              stats={facets.resolutions}
+            />
 
-        <BasicSeparator className="sw-my-4" />
+            <BasicSeparator className="sw-my-4" />
 
-        <LanguageFacet
-          fetching={this.props.loadingFacets.languages === true}
-          loadSearchResultCount={this.props.loadSearchResultCount}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.languages}
-          query={query}
-          referencedLanguages={this.props.referencedLanguages}
-          selectedLanguages={query.languages}
-          stats={facets.languages}
-        />
+            <StatusFacet
+              fetching={this.props.loadingFacets.statuses === true}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.statuses}
+              stats={facets.statuses}
+              statuses={query.statuses}
+            />
 
-        <BasicSeparator className="sw-my-4" />
+            <BasicSeparator className="sw-my-4" />
 
-        <RuleFacet
-          fetching={this.props.loadingFacets.rules === true}
-          loadSearchResultCount={this.props.loadSearchResultCount}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.rules}
-          query={query}
-          referencedRules={this.props.referencedRules}
-          stats={facets.rules}
-        />
+            <StandardFacet
+              cwe={query.cwe}
+              cweOpen={!!openFacets.cwe}
+              cweStats={facets.cwe}
+              fetchingCwe={this.props.loadingFacets.cwe === true}
+              fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true}
+              fetchingOwaspTop10-2021={this.props.loadingFacets['owaspTop10-2021'] === true}
+              fetchingSonarSourceSecurity={this.props.loadingFacets.sonarsourceSecurity === true}
+              loadSearchResultCount={this.props.loadSearchResultCount}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.standards}
+              owaspTop10={query.owaspTop10}
+              owaspTop10Open={!!openFacets.owaspTop10}
+              owaspTop10Stats={facets.owaspTop10}
+              owaspTop10-2021={query['owaspTop10-2021']}
+              owaspTop10-2021Open={!!openFacets['owaspTop10-2021']}
+              owaspTop10-2021Stats={facets['owaspTop10-2021']}
+              query={query}
+              sonarsourceSecurity={query.sonarsourceSecurity}
+              sonarsourceSecurityOpen={!!openFacets.sonarsourceSecurity}
+              sonarsourceSecurityStats={facets.sonarsourceSecurity}
+            />
 
-        <BasicSeparator className="sw-my-4" />
+            <BasicSeparator className="sw-my-4" />
 
-        <TagFacet
-          component={component}
-          branch={branch}
-          fetching={this.props.loadingFacets.tags === true}
-          loadSearchResultCount={this.props.loadSearchResultCount}
-          onChange={this.props.onFilterChange}
-          onToggle={this.props.onFacetToggle}
-          open={!!openFacets.tags}
-          query={query}
-          stats={facets.tags}
-          tags={query.tags}
-        />
+            <CreationDateFacet
+              component={component}
+              createdAfter={query.createdAfter}
+              createdAfterIncludesTime={createdAfterIncludesTime}
+              createdAt={query.createdAt}
+              createdBefore={query.createdBefore}
+              createdInLast={query.createdInLast}
+              fetching={this.props.loadingFacets.createdAt === true}
+              onChange={this.props.onFilterChange}
+              onToggle={this.props.onFacetToggle}
+              open={!!openFacets.createdAt}
+              inNewCodePeriod={query.inNewCodePeriod}
+              stats={facets.createdAt}
+            />
 
-        {displayProjectsFacet && (
-          <>
             <BasicSeparator className="sw-my-4" />
 
-            <ProjectFacet
-              component={component}
-              fetching={this.props.loadingFacets.projects === true}
+            <LanguageFacet
+              fetching={this.props.loadingFacets.languages === true}
               loadSearchResultCount={this.props.loadSearchResultCount}
               onChange={this.props.onFilterChange}
               onToggle={this.props.onFacetToggle}
-              open={!!openFacets.projects}
-              projects={query.projects}
+              open={!!openFacets.languages}
               query={query}
-              referencedComponents={this.props.referencedComponentsByKey}
-              stats={facets.projects}
+              referencedLanguages={this.props.referencedLanguages}
+              selectedLanguages={query.languages}
+              stats={facets.languages}
             />
-          </>
-        )}
-
-        {this.renderComponentFacets()}
 
-        {!this.props.myIssues && !disableDeveloperAggregatedInfo && (
-          <>
             <BasicSeparator className="sw-my-4" />
 
-            <AssigneeFacet
-              assigned={query.assigned}
-              assignees={query.assignees}
-              fetching={this.props.loadingFacets.assignees === true}
+            <RuleFacet
+              fetching={this.props.loadingFacets.rules === true}
               loadSearchResultCount={this.props.loadSearchResultCount}
               onChange={this.props.onFilterChange}
               onToggle={this.props.onFacetToggle}
-              open={!!openFacets.assignees}
+              open={!!openFacets.rules}
               query={query}
-              referencedUsers={this.props.referencedUsers}
-              stats={facets.assignees}
+              referencedRules={this.props.referencedRules}
+              stats={facets.rules}
             />
+
+            {!disableDeveloperAggregatedInfo && (
+              <>
+                <BasicSeparator className="sw-my-4" />
+
+                <TagFacet
+                  component={component}
+                  branch={branch}
+                  fetching={this.props.loadingFacets.tags === true}
+                  loadSearchResultCount={this.props.loadSearchResultCount}
+                  onChange={this.props.onFilterChange}
+                  onToggle={this.props.onFacetToggle}
+                  open={!!openFacets.tags}
+                  query={query}
+                  stats={facets.tags}
+                  tags={query.tags}
+                />
+
+                {displayProjectsFacet && (
+                  <>
+                    <BasicSeparator className="sw-my-4" />
+
+                    <ProjectFacet
+                      component={component}
+                      fetching={this.props.loadingFacets.projects === true}
+                      loadSearchResultCount={this.props.loadSearchResultCount}
+                      onChange={this.props.onFilterChange}
+                      onToggle={this.props.onFacetToggle}
+                      open={!!openFacets.projects}
+                      projects={query.projects}
+                      query={query}
+                      referencedComponents={this.props.referencedComponentsByKey}
+                      stats={facets.projects}
+                    />
+                  </>
+                )}
+
+                {this.renderComponentFacets()}
+
+                {!this.props.myIssues && (
+                  <>
+                    <BasicSeparator className="sw-my-4" />
+
+                    <AssigneeFacet
+                      assigned={query.assigned}
+                      assignees={query.assignees}
+                      fetching={this.props.loadingFacets.assignees === true}
+                      loadSearchResultCount={this.props.loadSearchResultCount}
+                      onChange={this.props.onFilterChange}
+                      onToggle={this.props.onFacetToggle}
+                      open={!!openFacets.assignees}
+                      query={query}
+                      referencedUsers={this.props.referencedUsers}
+                      stats={facets.assignees}
+                    />
+                  </>
+                )}
+
+                <BasicSeparator className="sw-my-4" />
+
+                <AuthorFacet
+                  author={query.author}
+                  component={component}
+                  fetching={this.props.loadingFacets.author === true}
+                  loadSearchResultCount={this.props.loadSearchResultCount}
+                  onChange={this.props.onFilterChange}
+                  onToggle={this.props.onFacetToggle}
+                  open={!!openFacets.author}
+                  query={query}
+                  stats={facets.author}
+                />
+              </>
+            )}
           </>
         )}
 
-        {!disableDeveloperAggregatedInfo && (
+        {needIssueSync && (
           <>
             <BasicSeparator className="sw-my-4" />
 
-            <AuthorFacet
-              author={query.author}
-              component={component}
-              fetching={this.props.loadingFacets.author === true}
-              loadSearchResultCount={this.props.loadSearchResultCount}
-              onChange={this.props.onFilterChange}
-              onToggle={this.props.onFacetToggle}
-              open={!!openFacets.author}
-              query={query}
-              stats={facets.author}
-            />
+            <FlagMessage className="sw-my-6" variant="info">
+              <div>
+                {translate('indexation.page_unavailable.description')}
+                <span className="sw-ml-1">
+                  <FormattedMessage
+                    defaultMessage={translate('indexation.filters_unavailable')}
+                    id="indexation.filters_unavailable"
+                    values={{
+                      link: (
+                        <Link to="https://docs.sonarqube.org/latest/instance-administration/reindexing/">
+                          {translate('learn_more')}
+                        </Link>
+                      ),
+                    }}
+                  />
+                </span>
+              </div>
+            </FlagMessage>
           </>
         )}
       </>
index 36b0a91c484e024063e4f199fbd81be0e9881bf6..cacde371aaa3e46fe6d012b2aa8dce9d92057eb0 100644 (file)
@@ -30,6 +30,7 @@ import { MultipleSelectionHint } from './MultipleSelectionHint';
 
 interface Props {
   fetching: boolean;
+  needIssueSync?: boolean;
   onChange: (changes: Partial<Query>) => void;
   onToggle: (property: string) => void;
   open: boolean;
@@ -79,6 +80,7 @@ export class TypeFacet extends React.PureComponent<Props> {
   }
 
   renderItem = (type: string) => {
+    const { needIssueSync } = this.props;
     const active = this.isFacetItemActive(type);
     const stat = this.getStat(type);
 
@@ -94,7 +96,7 @@ export class TypeFacet extends React.PureComponent<Props> {
         key={type}
         name={translate('issue.type', type)}
         onClick={this.handleItemClick}
-        stat={formatFacetStat(stat) ?? 0}
+        stat={(!needIssueSync && formatFacetStat(stat)) ?? 0}
         value={type}
       />
     );
index 77f8033af10b8d33e1c8aaf35b71943f0f386b3a..439fa6b25195024fa92c0d1060b974f99009b227 100644 (file)
@@ -67,6 +67,7 @@ export const ui = {
   scopeFacet: byRole('button', { name: 'issues.facet.scopes' }),
   statusFacet: byRole('button', { name: 'issues.facet.statuses' }),
   tagFacet: byRole('button', { name: 'issues.facet.tags' }),
+  typeFacet: byRole('button', { name: 'issues.facet.types' }),
 
   clearAssigneeFacet: byTestId('clear-issues.facet.assignees'),
   clearAuthorFacet: byTestId('clear-issues.facet.authors'),
index d2aee46d24437db925bb39568f1f42524dd46fe8..9638728d3abffd1a088ddcde682b7479a44459f5 100644 (file)
@@ -169,7 +169,7 @@ export function serializeQuery(query: Query): RawQuery {
 export const areQueriesEqual = (a: RawQuery, b: RawQuery) =>
   queriesEqual(parseQuery(a), parseQuery(b));
 
-export function parseFacets(facets: RawFacet[]): Dict<Facet> {
+export function parseFacets(facets?: RawFacet[]): Dict<Facet> {
   if (!facets) {
     return {};
   }
index f5c6f5d5f51eee637b1ea877f41c91a8a7e53840..342c09ea859fb1f1a07a7edd20616b379d7a45c9 100644 (file)
@@ -113,8 +113,11 @@ export function getBrancheLikesAsTree(branchLikes: BranchLike[]): BranchLikeTree
   }
 }
 
-export function getBranchLikeQuery(branchLike?: BranchLike): BranchParameters {
-  if (isBranch(branchLike) && !isMainBranch(branchLike)) {
+export function getBranchLikeQuery(
+  branchLike?: BranchLike,
+  includeMainBranch = false
+): BranchParameters {
+  if (isBranch(branchLike) && (includeMainBranch || !isMainBranch(branchLike))) {
     return { branch: branchLike.name };
   } else if (isPullRequest(branchLike)) {
     return { pullRequest: branchLike.key };
index 7b55991936b7ee379852aedd8d0099b7498387d3..57ce60737e1f297dc57ba12383137434c49b123c 100644 (file)
@@ -153,15 +153,28 @@ export interface RawIssuesResponse {
   users?: UserBase[];
 }
 
-export interface FetchIssuesPromise {
+export interface ListIssuesResponse {
   components: ReferencedComponent[];
-  effortTotal: number;
-  facets: RawFacet[];
+  issues: RawIssue[];
+  paging: Paging;
+  rules?: Array<{}>;
+}
+
+export interface FetchIssuesPromise {
+  components?: ReferencedComponent[];
+  effortTotal?: number;
+  facets?: RawFacet[];
+  issues: Issue[];
+  languages?: ReferencedLanguage[];
+  paging: Paging;
+  rules: ReferencedRule[];
+  users?: UserBase[];
+}
+
+export interface ListIssuesPromise {
   issues: Issue[];
-  languages: ReferencedLanguage[];
   paging: Paging;
   rules: ReferencedRule[];
-  users: UserBase[];
 }
 
 export interface ReferencedComponent {
index b43d1ea5fc3e37c411734d7b77679ee60117325f..e7b5f022536b215b14e29a7e6cd2c40a4dd4ddda 100644 (file)
@@ -4741,6 +4741,8 @@ indexation.page_unavailable.description.additional_information=This page is unav
 indexation.filter_unavailable.description=This filter is unavailable until this process is complete.
 indexation.learn_more=Learn more:
 indexation.reindexing=Reindexing
+indexation.filters_unavailable=Some filters are unavailable until this process is complete. {link}
+
 
 #------------------------------------------------------------------------------
 #