Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Snapshot and Restore] Server side snapshots pagination #110266

Merged
merged 38 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2c2065f
[Snapshot % Restore] Added server side pagination and sorting to get …
yuliacech Aug 26, 2021
6b565fe
Merge branch 'master' into snapshots_sorting
kibanamachine Aug 30, 2021
25c14d0
[Snapshot & Restore] Added server side sorting by shards and failed s…
yuliacech Aug 30, 2021
e899c1e
[Snapshot & Restore] Fixed i18n errors
yuliacech Aug 30, 2021
a76a350
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 3, 2021
fbc1073
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 13, 2021
fcf9420
[Snapshot & Restore] Added server side sorting by repository
yuliacech Sep 14, 2021
5546ee3
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 15, 2021
a8418f3
[Snapshot & Restore] Implemented server side search request for snaps…
yuliacech Sep 21, 2021
989dbf9
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 21, 2021
6fab678
[Snapshot & Restore] Fixed eslint errors
yuliacech Sep 21, 2021
85d6b5f
[Snapshot & Restore] Removed uncommented code
yuliacech Sep 21, 2021
f84dfd5
[Snapshot & Restore] Fixed pagination/search bug
yuliacech Sep 21, 2021
02ea7c1
[Snapshot & Restore] Fixed pagination/search bug
yuliacech Sep 21, 2021
16891ce
[Snapshot & Restore] Fixed text truncate bug
yuliacech Sep 21, 2021
1334f40
[Snapshot & Restore] Fixed non existent repository search error
yuliacech Sep 21, 2021
7b0d517
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 23, 2021
35e1acf
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 27, 2021
38cc772
Merge branch 'master' into snapshots_sorting
kibanamachine Sep 29, 2021
9ca9a08
Update x-pack/plugins/snapshot_restore/public/application/sections/ho…
yuliacech Sep 29, 2021
5db21ad
Update x-pack/plugins/snapshot_restore/public/application/sections/ho…
yuliacech Sep 29, 2021
b1aa490
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 4, 2021
56be367
[Snapshot & Restore] Fixed missing i18n and no snapshots callout
yuliacech Oct 4, 2021
c9467f7
[Snapshot & Restore] Moved "getSnapshotSearchWildcard" to a separate …
yuliacech Oct 4, 2021
5e84030
[Snapshot & Restore] Added api integration tests for "get snapshots" …
yuliacech Oct 4, 2021
d2d6a56
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 5, 2021
e920e51
[Snapshot & Restore] Renamed SnapshotSearchOptions/SnapshotTableOptio…
yuliacech Oct 5, 2021
1feb0c1
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 7, 2021
cb22230
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 7, 2021
5d86bd7
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 11, 2021
c0c8096
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 12, 2021
347f0e4
[Snapshot & Restore] Fixed search wildcard to also match string in th…
yuliacech Oct 12, 2021
cfd5f04
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 14, 2021
61d21d4
[Snapshot & Restore] Added incremental search back to snapshots list …
yuliacech Oct 14, 2021
467f47c
[Snapshot & Restore] Updated snapshot search debounce value and extra…
yuliacech Oct 15, 2021
9ac27d3
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 18, 2021
e139f95
[Snapshot & Restore] Renamed debounceValue to cachedListParams and ad…
yuliacech Oct 18, 2021
2d08704
Merge branch 'master' into snapshots_sorting
kibanamachine Oct 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import sinon, { SinonFakeServer } from 'sinon';
import { API_BASE_PATH } from '../../../common/constants';
import { API_BASE_PATH } from '../../../common';

type HttpResponse = Record<string, any> | any[];

Expand Down Expand Up @@ -54,7 +54,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
};

const setLoadSnapshotsResponse = (response: HttpResponse = {}) => {
const defaultResponse = { errors: {}, snapshots: [], repositories: [] };
const defaultResponse = { errors: {}, snapshots: [], repositories: [], total: 0 };

server.respondWith('GET', `${API_BASE_PATH}snapshots`, mockResponse(defaultResponse, response));
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { act } from 'react-dom/test-utils';
import * as fixtures from '../../test/fixtures';
import { SNAPSHOT_STATE } from '../../public/application/constants';
import { API_BASE_PATH } from '../../common/constants';
import { API_BASE_PATH } from '../../common';
import {
setupEnvironment,
pageHelpers,
Expand Down Expand Up @@ -431,6 +431,7 @@ describe('<SnapshotRestoreHome />', () => {
httpRequestsMockHelpers.setLoadSnapshotsResponse({
snapshots: [],
repositories: ['my-repo'],
total: 0,
});

testBed = await setup();
Expand Down Expand Up @@ -469,6 +470,7 @@ describe('<SnapshotRestoreHome />', () => {
httpRequestsMockHelpers.setLoadSnapshotsResponse({
snapshots,
repositories: [REPOSITORY_NAME],
total: 2,
});

testBed = await setup();
Expand Down Expand Up @@ -501,18 +503,10 @@ describe('<SnapshotRestoreHome />', () => {
});
});

test('should show a warning if the number of snapshots exceeded the limit', () => {
// We have mocked the SNAPSHOT_LIST_MAX_SIZE to 2, so the warning should display
const { find, exists } = testBed;
expect(exists('maxSnapshotsWarning')).toBe(true);
expect(find('maxSnapshotsWarning').text()).toContain(
'Cannot show the full list of snapshots'
);
});

test('should show a warning if one repository contains errors', async () => {
httpRequestsMockHelpers.setLoadSnapshotsResponse({
snapshots,
total: 2,
repositories: [REPOSITORY_NAME],
errors: {
repository_with_errors: {
Expand Down
6 changes: 0 additions & 6 deletions x-pack/plugins/snapshot_restore/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,3 @@ export const TIME_UNITS: { [key: string]: 'd' | 'h' | 'm' | 's' } = {
MINUTE: 'm',
SECOND: 's',
};

/**
* [Temporary workaround] In order to prevent client-side performance issues for users with a large number of snapshots,
* we set a hard-coded limit on the number of snapshots we return from the ES snapshots API
*/
export const SNAPSHOT_LIST_MAX_SIZE = 1000;
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,12 @@
*/

export { useDecodedParams } from './use_decoded_params';

export {
SortField,
SortDirection,
SnapshotListParams,
getListParams,
getQueryFromListParams,
DEFAULT_SNAPSHOT_LIST_PARAMS,
} from './snapshot_list_params';
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Direction, Query } from '@elastic/eui';

export type SortField =
| 'snapshot'
| 'repository'
| 'indices'
| 'startTimeInMillis'
| 'durationInMillis'
| 'shards.total'
| 'shards.failed';

export type SortDirection = Direction;

interface SnapshotTableParams {
sortField: SortField;
sortDirection: SortDirection;
pageIndex: number;
pageSize: number;
}
interface SnapshotSearchParams {
searchField?: string;
searchValue?: string;
searchMatch?: string;
searchOperator?: string;
}
export type SnapshotListParams = SnapshotTableParams & SnapshotSearchParams;

// By default, we'll display the most recent snapshots at the top of the table (no query).
export const DEFAULT_SNAPSHOT_LIST_PARAMS: SnapshotListParams = {
sortField: 'startTimeInMillis',
sortDirection: 'desc',
pageIndex: 0,
pageSize: 20,
};

const resetSearchOptions = (listParams: SnapshotListParams): SnapshotListParams => ({
...listParams,
searchField: undefined,
searchValue: undefined,
searchMatch: undefined,
searchOperator: undefined,
});

// to init the query for repository and policyName search passed via url
export const getQueryFromListParams = (listParams: SnapshotListParams): Query => {
const { searchField, searchValue } = listParams;
if (!searchField || !searchValue) {
return Query.MATCH_ALL;
}
return Query.parse(`${searchField}=${searchValue}`);
};

export const getListParams = (listParams: SnapshotListParams, query: Query): SnapshotListParams => {
if (!query) {
return resetSearchOptions(listParams);
}
const clause = query.ast.clauses[0];
if (!clause) {
return resetSearchOptions(listParams);
}
// term queries (free word search) converts to snapshot name search
if (clause.type === 'term') {
return {
...listParams,
searchField: 'snapshot',
searchValue: String(clause.value),
searchMatch: clause.match,
searchOperator: 'eq',
};
}
if (clause.type === 'field') {
return {
...listParams,
searchField: clause.field,
searchValue: String(clause.value),
searchMatch: clause.match,
searchOperator: clause.operator,
};
}
return resetSearchOptions(listParams);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@
*/

export { SnapshotTable } from './snapshot_table';
export { RepositoryError } from './repository_error';
export { RepositoryEmptyPrompt } from './repository_empty_prompt';
export { SnapshotEmptyPrompt } from './snapshot_empty_prompt';
export { SnapshotSearchBar } from './snapshot_search_bar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { useHistory } from 'react-router-dom';
import { EuiButton, EuiEmptyPrompt, EuiPageContent } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { reactRouterNavigate } from '../../../../../shared_imports';
import { linkToAddRepository } from '../../../../services/navigation';

export const RepositoryEmptyPrompt: React.FunctionComponent = () => {
const history = useHistory();
return (
<EuiPageContent
hasShadow={false}
paddingSize="none"
verticalPosition="center"
horizontalPosition="center"
data-test-subj="snapshotListEmpty"
>
<EuiEmptyPrompt
iconType="managementApp"
title={
<h1 data-test-subj="title">
<FormattedMessage
id="xpack.snapshotRestore.snapshotList.emptyPrompt.noRepositoriesTitle"
defaultMessage="Start by registering a repository"
/>
</h1>
}
body={
<>
<p>
<FormattedMessage
id="xpack.snapshotRestore.snapshotList.emptyPrompt.noRepositoriesDescription"
defaultMessage="You need a place where your snapshots will live."
/>
</p>
<p>
<EuiButton
{...reactRouterNavigate(history, linkToAddRepository())}
fill
iconType="plusInCircle"
data-test-subj="registerRepositoryButton"
>
<FormattedMessage
id="xpack.snapshotRestore.snapshotList.emptyPrompt.noRepositoriesAddButtonLabel"
defaultMessage="Register a repository"
/>
</EuiButton>
</p>
</>
}
data-test-subj="emptyPrompt"
/>
</EuiPageContent>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { useHistory } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiEmptyPrompt, EuiLink, EuiPageContent } from '@elastic/eui';
import { reactRouterNavigate } from '../../../../../shared_imports';
import { linkToRepositories } from '../../../../services/navigation';

export const RepositoryError: React.FunctionComponent = () => {
const history = useHistory();
return (
<EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<EuiEmptyPrompt
iconType="managementApp"
data-test-subj="repositoryErrorsPrompt"
title={
<h1 data-test-subj="title">
<FormattedMessage
id="xpack.snapshotRestore.snapshotList.emptyPrompt.errorRepositoriesTitle"
defaultMessage="Some repositories contain errors"
/>
</h1>
}
body={
<p>
<FormattedMessage
id="xpack.snapshotRestore.snapshotList.emptyPrompt.repositoryWarningDescription"
defaultMessage="Go to {repositoryLink} to fix the errors."
values={{
repositoryLink: (
<EuiLink {...reactRouterNavigate(history, linkToRepositories())}>
<FormattedMessage
id="xpack.snapshotRestore.repositoryWarningLinkText"
defaultMessage="Repositories"
/>
</EuiLink>
),
}}
/>
</p>
}
/>
</EuiPageContent>
);
};
Loading