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 & Restore] Decode URIs and fix editing of a policy #76278

Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { deserializeSnapshotDetails } from './snapshot_serialization';
import { deserializeSnapshotDetails, serializeSnapshotConfig } from './snapshot_serialization';

describe('deserializeSnapshotDetails', () => {
test('deserializes a snapshot', () => {
expect(
deserializeSnapshotDetails(
'repositoryName',
{
snapshot: 'snapshot name',
uuid: 'UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
include_global_state: false,
state: 'SUCCESS',
start_time: '0',
start_time_in_millis: 0,
end_time: '1',
end_time_in_millis: 1,
duration_in_millis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
failures: [
{
index: 'z',
shard: 1,
},
{
index: 'a',
shard: 3,
},
{
index: 'a',
shard: 1,
},
{
index: 'a',
shard: 2,
},
],
},
'found-snapshots',
[
describe('Snapshot serialization and deserialization', () => {
describe('deserializeSnapshotDetails', () => {
test('deserializes a snapshot', () => {
expect(
deserializeSnapshotDetails(
'repositoryName',
{
snapshot: 'last_successful_snapshot',
uuid: 'last_successful_snapshot_UUID',
snapshot: 'snapshot name',
uuid: 'UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
Expand Down Expand Up @@ -87,56 +49,109 @@ describe('deserializeSnapshotDetails', () => {
},
],
},
]
)
).toEqual({
repository: 'repositoryName',
snapshot: 'snapshot name',
uuid: 'UUID',
versionId: 5,
version: 'version',
// Indices are sorted.
indices: ['index1', 'index2', 'index3'],
dataStreams: [],
includeGlobalState: false,
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
indexFailures: [
{
index: 'a',
failures: [
'found-snapshots',
[
{
shard: 1,
},
{
shard: 2,
},
{
shard: 3,
},
],
},
{
index: 'z',
failures: [
{
shard: 1,
snapshot: 'last_successful_snapshot',
uuid: 'last_successful_snapshot_UUID',
version_id: 5,
version: 'version',
indices: ['index2', 'index3', 'index1'],
include_global_state: false,
state: 'SUCCESS',
start_time: '0',
start_time_in_millis: 0,
end_time: '1',
end_time_in_millis: 1,
duration_in_millis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
failures: [
{
index: 'z',
shard: 1,
},
{
index: 'a',
shard: 3,
},
{
index: 'a',
shard: 1,
},
{
index: 'a',
shard: 2,
},
],
},
],
]
)
).toEqual({
repository: 'repositoryName',
snapshot: 'snapshot name',
uuid: 'UUID',
versionId: 5,
version: 'version',
// Indices are sorted.
indices: ['index1', 'index2', 'index3'],
dataStreams: [],
includeGlobalState: false,
// Failures are grouped and sorted by index, and the failures themselves are sorted by shard.
indexFailures: [
{
index: 'a',
failures: [
{
shard: 1,
},
{
shard: 2,
},
{
shard: 3,
},
],
},
{
index: 'z',
failures: [
{
shard: 1,
},
],
},
],
state: 'SUCCESS',
startTime: '0',
startTimeInMillis: 0,
endTime: '1',
endTimeInMillis: 1,
durationInMillis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
],
state: 'SUCCESS',
startTime: '0',
startTimeInMillis: 0,
endTime: '1',
endTimeInMillis: 1,
durationInMillis: 1,
shards: {
total: 3,
failed: 1,
successful: 2,
},
managedRepository: 'found-snapshots',
isLastSuccessfulSnapshot: false,
managedRepository: 'found-snapshots',
isLastSuccessfulSnapshot: false,
});
});
});

describe('serializeSnapshotConfig', () => {
test('serializes config as expected', () => {
const metadata = { test: 'what have you' };
expect(serializeSnapshotConfig({ metadata, indices: '.k*' })).toEqual({
metadata,
indices: ['.k*'],
});
});
test('serializes empty config as expected', () => {
expect(serializeSnapshotConfig({})).toEqual({});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ export function deserializeSnapshotConfig(snapshotConfigEs: SnapshotConfigEs): S
export function serializeSnapshotConfig(snapshotConfig: SnapshotConfig): SnapshotConfigEs {
const { indices, ignoreUnavailable, includeGlobalState, partial, metadata } = snapshotConfig;

const indicesArray = csvToArray(indices);
const maybeIndicesArray = csvToArray(indices);

const snapshotConfigEs: SnapshotConfigEs = {
indices: indicesArray,
indices: maybeIndicesArray,
ignore_unavailable: ignoreUnavailable,
include_global_state: includeGlobalState,
partial,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/snapshot_restore/common/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const csvToArray = (indices?: string | string[]): string[] => {
export const csvToArray = (indices?: string | string[]): string[] | undefined => {
return indices && Array.isArray(indices)
? indices
: typeof indices === 'string'
? indices.split(',')
: [];
: undefined;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const maximumItemPreviewCount = 10;

export const useCollapsibleList = ({ items }: Arg): ReturnValue => {
const [isShowingFullList, setIsShowingFullList] = useState<boolean>(false);
const itemsArray = csvToArray(items);
const itemsArray = csvToArray(items) ?? [];
const displayItems: ChildItems =
items === undefined
? 'all'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ export const PolicyStepRetention: React.FunctionComponent<StepProps> = ({
}) => {
const { retention = {} } = policy;

const updatePolicyRetention = (updatedFields: Partial<SlmPolicyPayload['retention']>): void => {
const updatePolicyRetention = (
updatedFields: Partial<SlmPolicyPayload['retention']>,
validationHelperData = {}
): void => {
const newRetention = { ...retention, ...updatedFields };
updatePolicy({
retention: newRetention,
});
updatePolicy(
{
retention: newRetention,
},
validationHelperData
);
};

// State for touched inputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import { SlmPolicyPayload } from '../../../../../../../../common/types';
import { useServices } from '../../../../../../app_context';
import { PolicyValidation } from '../../../../../../services/validation';
import { PolicyValidation, ValidatePolicyHelperData } from '../../../../../../services/validation';

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

import { DataStreamsAndIndicesListHelpText } from './data_streams_and_indices_list_help_text';

interface IndicesConfig {
indices?: string[] | string;
}

interface Props {
isManagedPolicy: boolean;
policy: SlmPolicyPayload;
indices: string[];
dataStreams: string[];
onUpdate: (arg: { indices?: string[] | string }) => void;
onUpdate: (arg: IndicesConfig, validateHelperData: ValidatePolicyHelperData) => void;
errors: PolicyValidation['errors'];
}

Expand All @@ -53,7 +57,7 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
dataStreams,
indices,
policy,
onUpdate,
onUpdate: _onUpdate,
errors,
}) => {
const { i18n } = useServices();
Expand All @@ -66,6 +70,12 @@ export const IndicesAndDataStreamsField: FunctionComponent<Props> = ({
!config.indices || (Array.isArray(config.indices) && config.indices.length === 0)
);

const onUpdate = (data: IndicesConfig) => {
_onUpdate(data, {
validateIndicesCount: !isAllIndices,
});
};

const [indicesAndDataStreamsSelection, setIndicesAndDataStreamsSelection] = useState<string[]>(
() =>
Array.isArray(config.indices) && !isAllIndices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ export const PolicyStepSettings: React.FunctionComponent<StepProps> = ({
}) => {
const { config = {}, isManagedPolicy } = policy;

const updatePolicyConfig = (updatedFields: Partial<SlmPolicyPayload['config']>): void => {
const updatePolicyConfig = (
updatedFields: Partial<SlmPolicyPayload['config']>,
validationHelperData = {}
): void => {
const newConfig = { ...config, ...updatedFields };
updatePolicy({
config: newConfig,
});
updatePolicy(
{
config: newConfig,
},
validationHelperData
);
};

const renderIgnoreUnavailableField = () => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export const RestoreSnapshotStepLogistics: React.FunctionComponent<StepProps> =
});
}
}}
selectedIndicesAndDataStreams={csvToArray(restoreIndices)}
selectedIndicesAndDataStreams={csvToArray(restoreIndices) ?? []}
indices={snapshotIndices}
dataStreams={snapshotDataStreams}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const attemptToURIDecode = (value: string) => {
let result: string;

try {
result = decodeURI(value);
result = decodeURIComponent(result);
} catch (e1) {
try {
result = decodeURIComponent(value);
} catch (e2) {
result = value;
}
}

return result;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { useDecodedParams } from './use_decoded_params';
Loading