Skip to content

Commit f583c4e

Browse files
[Snapshot & Restore] Decode URIs and fix editing of a policy (#76278) (#76322)
* fix URI decoding and editing of a policy which backs up all indices * fix type issue * fix general use of encoding and update decode algo * fix serialisation of snapshots and added a test * Fix test description name * Update attempt_to_uri_decode.ts * catch errors from decoding in the already throwing code Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent db735b3 commit f583c4e

24 files changed

Lines changed: 277 additions & 163 deletions

File tree

x-pack/plugins/snapshot_restore/common/lib/snapshot_serialization.test.ts

Lines changed: 109 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,17 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { deserializeSnapshotDetails } from './snapshot_serialization';
7+
import { deserializeSnapshotDetails, serializeSnapshotConfig } from './snapshot_serialization';
88

9-
describe('deserializeSnapshotDetails', () => {
10-
test('deserializes a snapshot', () => {
11-
expect(
12-
deserializeSnapshotDetails(
13-
'repositoryName',
14-
{
15-
snapshot: 'snapshot name',
16-
uuid: 'UUID',
17-
version_id: 5,
18-
version: 'version',
19-
indices: ['index2', 'index3', 'index1'],
20-
include_global_state: false,
21-
state: 'SUCCESS',
22-
start_time: '0',
23-
start_time_in_millis: 0,
24-
end_time: '1',
25-
end_time_in_millis: 1,
26-
duration_in_millis: 1,
27-
shards: {
28-
total: 3,
29-
failed: 1,
30-
successful: 2,
31-
},
32-
failures: [
33-
{
34-
index: 'z',
35-
shard: 1,
36-
},
37-
{
38-
index: 'a',
39-
shard: 3,
40-
},
41-
{
42-
index: 'a',
43-
shard: 1,
44-
},
45-
{
46-
index: 'a',
47-
shard: 2,
48-
},
49-
],
50-
},
51-
'found-snapshots',
52-
[
9+
describe('Snapshot serialization and deserialization', () => {
10+
describe('deserializeSnapshotDetails', () => {
11+
test('deserializes a snapshot', () => {
12+
expect(
13+
deserializeSnapshotDetails(
14+
'repositoryName',
5315
{
54-
snapshot: 'last_successful_snapshot',
55-
uuid: 'last_successful_snapshot_UUID',
16+
snapshot: 'snapshot name',
17+
uuid: 'UUID',
5618
version_id: 5,
5719
version: 'version',
5820
indices: ['index2', 'index3', 'index1'],
@@ -87,56 +49,109 @@ describe('deserializeSnapshotDetails', () => {
8749
},
8850
],
8951
},
90-
]
91-
)
92-
).toEqual({
93-
repository: 'repositoryName',
94-
snapshot: 'snapshot name',
95-
uuid: 'UUID',
96-
versionId: 5,
97-
version: 'version',
98-
// Indices are sorted.
99-
indices: ['index1', 'index2', 'index3'],
100-
dataStreams: [],
101-
includeGlobalState: false,
102-
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
103-
indexFailures: [
104-
{
105-
index: 'a',
106-
failures: [
52+
'found-snapshots',
53+
[
10754
{
108-
shard: 1,
109-
},
110-
{
111-
shard: 2,
112-
},
113-
{
114-
shard: 3,
115-
},
116-
],
117-
},
118-
{
119-
index: 'z',
120-
failures: [
121-
{
122-
shard: 1,
55+
snapshot: 'last_successful_snapshot',
56+
uuid: 'last_successful_snapshot_UUID',
57+
version_id: 5,
58+
version: 'version',
59+
indices: ['index2', 'index3', 'index1'],
60+
include_global_state: false,
61+
state: 'SUCCESS',
62+
start_time: '0',
63+
start_time_in_millis: 0,
64+
end_time: '1',
65+
end_time_in_millis: 1,
66+
duration_in_millis: 1,
67+
shards: {
68+
total: 3,
69+
failed: 1,
70+
successful: 2,
71+
},
72+
failures: [
73+
{
74+
index: 'z',
75+
shard: 1,
76+
},
77+
{
78+
index: 'a',
79+
shard: 3,
80+
},
81+
{
82+
index: 'a',
83+
shard: 1,
84+
},
85+
{
86+
index: 'a',
87+
shard: 2,
88+
},
89+
],
12390
},
124-
],
91+
]
92+
)
93+
).toEqual({
94+
repository: 'repositoryName',
95+
snapshot: 'snapshot name',
96+
uuid: 'UUID',
97+
versionId: 5,
98+
version: 'version',
99+
// Indices are sorted.
100+
indices: ['index1', 'index2', 'index3'],
101+
dataStreams: [],
102+
includeGlobalState: false,
103+
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
104+
indexFailures: [
105+
{
106+
index: 'a',
107+
failures: [
108+
{
109+
shard: 1,
110+
},
111+
{
112+
shard: 2,
113+
},
114+
{
115+
shard: 3,
116+
},
117+
],
118+
},
119+
{
120+
index: 'z',
121+
failures: [
122+
{
123+
shard: 1,
124+
},
125+
],
126+
},
127+
],
128+
state: 'SUCCESS',
129+
startTime: '0',
130+
startTimeInMillis: 0,
131+
endTime: '1',
132+
endTimeInMillis: 1,
133+
durationInMillis: 1,
134+
shards: {
135+
total: 3,
136+
failed: 1,
137+
successful: 2,
125138
},
126-
],
127-
state: 'SUCCESS',
128-
startTime: '0',
129-
startTimeInMillis: 0,
130-
endTime: '1',
131-
endTimeInMillis: 1,
132-
durationInMillis: 1,
133-
shards: {
134-
total: 3,
135-
failed: 1,
136-
successful: 2,
137-
},
138-
managedRepository: 'found-snapshots',
139-
isLastSuccessfulSnapshot: false,
139+
managedRepository: 'found-snapshots',
140+
isLastSuccessfulSnapshot: false,
141+
});
142+
});
143+
});
144+
145+
describe('serializeSnapshotConfig', () => {
146+
test('serializes config as expected', () => {
147+
const metadata = { test: 'what have you' };
148+
expect(serializeSnapshotConfig({ metadata, indices: '.k*' })).toEqual({
149+
metadata,
150+
indices: ['.k*'],
151+
});
152+
});
153+
test('serializes empty config as expected', () => {
154+
expect(serializeSnapshotConfig({})).toEqual({});
140155
});
141156
});
142157
});

x-pack/plugins/snapshot_restore/common/lib/snapshot_serialization.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ export function deserializeSnapshotConfig(snapshotConfigEs: SnapshotConfigEs): S
131131
export function serializeSnapshotConfig(snapshotConfig: SnapshotConfig): SnapshotConfigEs {
132132
const { indices, ignoreUnavailable, includeGlobalState, partial, metadata } = snapshotConfig;
133133

134-
const indicesArray = csvToArray(indices);
134+
const maybeIndicesArray = csvToArray(indices);
135135

136136
const snapshotConfigEs: SnapshotConfigEs = {
137-
indices: indicesArray,
137+
indices: maybeIndicesArray,
138138
ignore_unavailable: ignoreUnavailable,
139139
include_global_state: includeGlobalState,
140140
partial,

x-pack/plugins/snapshot_restore/common/lib/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
export const csvToArray = (indices?: string | string[]): string[] => {
7+
export const csvToArray = (indices?: string | string[]): string[] | undefined => {
88
return indices && Array.isArray(indices)
99
? indices
1010
: typeof indices === 'string'
1111
? indices.split(',')
12-
: [];
12+
: undefined;
1313
};

x-pack/plugins/snapshot_restore/public/application/components/collapsible_lists/use_collapsible_list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const maximumItemPreviewCount = 10;
2424

2525
export const useCollapsibleList = ({ items }: Arg): ReturnValue => {
2626
const [isShowingFullList, setIsShowingFullList] = useState<boolean>(false);
27-
const itemsArray = csvToArray(items);
27+
const itemsArray = csvToArray(items) ?? [];
2828
const displayItems: ChildItems =
2929
items === undefined
3030
? 'all'

x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_retention.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,17 @@ export const PolicyStepRetention: React.FunctionComponent<StepProps> = ({
3535
}) => {
3636
const { retention = {} } = policy;
3737

38-
const updatePolicyRetention = (updatedFields: Partial<SlmPolicyPayload['retention']>): void => {
38+
const updatePolicyRetention = (
39+
updatedFields: Partial<SlmPolicyPayload['retention']>,
40+
validationHelperData = {}
41+
): void => {
3942
const newRetention = { ...retention, ...updatedFields };
40-
updatePolicy({
41-
retention: newRetention,
42-
});
43+
updatePolicy(
44+
{
45+
retention: newRetention,
46+
},
47+
validationHelperData
48+
);
4349
};
4450

4551
// State for touched inputs

x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_settings/fields/indices_and_data_streams_field/indices_and_data_streams_field.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525

2626
import { SlmPolicyPayload } from '../../../../../../../../common/types';
2727
import { useServices } from '../../../../../../app_context';
28-
import { PolicyValidation } from '../../../../../../services/validation';
28+
import { PolicyValidation, ValidatePolicyHelperData } from '../../../../../../services/validation';
2929

3030
import { orderDataStreamsAndIndices } from '../../../../../lib';
3131
import { DataStreamBadge } from '../../../../../data_stream_badge';
@@ -34,12 +34,16 @@ import { mapSelectionToIndicesOptions, determineListMode } from './helpers';
3434

3535
import { DataStreamsAndIndicesListHelpText } from './data_streams_and_indices_list_help_text';
3636

37+
interface IndicesConfig {
38+
indices?: string[] | string;
39+
}
40+
3741
interface Props {
3842
isManagedPolicy: boolean;
3943
policy: SlmPolicyPayload;
4044
indices: string[];
4145
dataStreams: string[];
42-
onUpdate: (arg: { indices?: string[] | string }) => void;
46+
onUpdate: (arg: IndicesConfig, validateHelperData: ValidatePolicyHelperData) => void;
4347
errors: PolicyValidation['errors'];
4448
}
4549

@@ -53,7 +57,7 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
5357
dataStreams,
5458
indices,
5559
policy,
56-
onUpdate,
60+
onUpdate: _onUpdate,
5761
errors,
5862
}) => {
5963
const { i18n } = useServices();
@@ -66,6 +70,12 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
6670
!config.indices || (Array.isArray(config.indices) && config.indices.length === 0)
6771
);
6872

73+
const onUpdate = (data: IndicesConfig) => {
74+
_onUpdate(data, {
75+
validateIndicesCount: !isAllIndices,
76+
});
77+
};
78+
6979
const [indicesAndDataStreamsSelection, setIndicesAndDataStreamsSelection] = useState<string[]>(
7080
() =>
7181
Array.isArray(config.indices) && !isAllIndices

x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_settings/step_settings.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,17 @@ export const PolicyStepSettings: React.FunctionComponent<StepProps> = ({
3131
}) => {
3232
const { config = {}, isManagedPolicy } = policy;
3333

34-
const updatePolicyConfig = (updatedFields: Partial<SlmPolicyPayload['config']>): void => {
34+
const updatePolicyConfig = (
35+
updatedFields: Partial<SlmPolicyPayload['config']>,
36+
validationHelperData = {}
37+
): void => {
3538
const newConfig = { ...config, ...updatedFields };
36-
updatePolicy({
37-
config: newConfig,
38-
});
39+
updatePolicy(
40+
{
41+
config: newConfig,
42+
},
43+
validationHelperData
44+
);
3945
};
4046

4147
const renderIgnoreUnavailableField = () => (

x-pack/plugins/snapshot_restore/public/application/components/restore_snapshot_form/steps/step_logistics/step_logistics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ export const RestoreSnapshotStepLogistics: React.FunctionComponent<StepProps> =
311311
});
312312
}
313313
}}
314-
selectedIndicesAndDataStreams={csvToArray(restoreIndices)}
314+
selectedIndicesAndDataStreams={csvToArray(restoreIndices) ?? []}
315315
indices={snapshotIndices}
316316
dataStreams={snapshotDataStreams}
317317
/>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export const attemptToURIDecode = (value: string) => {
8+
let result: string;
9+
10+
try {
11+
result = decodeURI(value);
12+
result = decodeURIComponent(result);
13+
} catch (e1) {
14+
try {
15+
result = decodeURIComponent(value);
16+
} catch (e2) {
17+
result = value;
18+
}
19+
}
20+
21+
return result;
22+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export { useDecodedParams } from './use_decoded_params';

0 commit comments

Comments
 (0)