mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[SR] Prevent snapshots in Cloud-managed repository from being deleted in the UI (#40104)
* Extract `getManagedRepositoryName` to `lib/` * Prevent managed repository snapshots from being deleted in table UI * Prevent delete of managed repository snapshot from its details UI * Fix test * PR feedback and empty restore tab copy edits
This commit is contained in:
parent
0a6e22103b
commit
2eeea97fa8
13 changed files with 156 additions and 35 deletions
|
@ -22,6 +22,7 @@ export interface SnapshotDetails {
|
||||||
durationInMillis: number;
|
durationInMillis: number;
|
||||||
indexFailures: any[];
|
indexFailures: any[];
|
||||||
shards: SnapshotDetailsShardsStatus;
|
shards: SnapshotDetailsShardsStatus;
|
||||||
|
isManagedRepository?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SnapshotDetailsShardsStatus {
|
interface SnapshotDetailsShardsStatus {
|
||||||
|
|
|
@ -15,9 +15,10 @@ import {
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiSpacer,
|
EuiSpacer,
|
||||||
EuiLoadingSpinner,
|
EuiLoadingSpinner,
|
||||||
|
EuiLink,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { SectionError, SectionLoading } from '../../../components';
|
import { SectionError, SectionLoading } from '../../../components';
|
||||||
import { UIM_RESTORE_LIST_LOAD } from '../../../constants';
|
import { UIM_RESTORE_LIST_LOAD, BASE_PATH } from '../../../constants';
|
||||||
import { useAppDependencies } from '../../../index';
|
import { useAppDependencies } from '../../../index';
|
||||||
import { useLoadRestores } from '../../../services/http';
|
import { useLoadRestores } from '../../../services/http';
|
||||||
import { useAppState } from '../../../services/state';
|
import { useAppState } from '../../../services/state';
|
||||||
|
@ -123,7 +124,7 @@ export const RestoreList: React.FunctionComponent = () => {
|
||||||
<h1>
|
<h1>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.snapshotRestore.restoreList.emptyPromptTitle"
|
id="xpack.snapshotRestore.restoreList.emptyPromptTitle"
|
||||||
defaultMessage="You don't have any snapshot restores"
|
defaultMessage="You don't have any restored snapshots"
|
||||||
/>
|
/>
|
||||||
</h1>
|
</h1>
|
||||||
}
|
}
|
||||||
|
@ -132,7 +133,17 @@ export const RestoreList: React.FunctionComponent = () => {
|
||||||
<p>
|
<p>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.snapshotRestore.restoreList.emptyPromptDescription"
|
id="xpack.snapshotRestore.restoreList.emptyPromptDescription"
|
||||||
defaultMessage="Track progress of indices that are restored from snapshots."
|
defaultMessage="Go to {snapshotsLink} to start a restore."
|
||||||
|
values={{
|
||||||
|
snapshotsLink: (
|
||||||
|
<EuiLink href={`#${BASE_PATH}/snapshots`}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.snapshotRestore.restoreList.emptyPromptDescriptionLink"
|
||||||
|
defaultMessage="Snapshots"
|
||||||
|
/>
|
||||||
|
</EuiLink>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
|
@ -81,6 +81,7 @@ export const ShardsTable: React.FunctionComponent<Props> = ({ shards }) => {
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.snapshotRestore.restoreList.shardTable.primaryAbbreviationText"
|
id="xpack.snapshotRestore.restoreList.shardTable.primaryAbbreviationText"
|
||||||
defaultMessage="P"
|
defaultMessage="P"
|
||||||
|
description="Used as an abbreviation for 'Primary', as in 'Primary shard'"
|
||||||
/>
|
/>
|
||||||
</strong>
|
</strong>
|
||||||
</EuiToolTip>
|
</EuiToolTip>
|
||||||
|
|
|
@ -199,6 +199,18 @@ export const SnapshotDetails: React.FunctionComponent<Props> = ({
|
||||||
onSnapshotDeleted
|
onSnapshotDeleted
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
isDisabled={snapshotDetails.isManagedRepository}
|
||||||
|
title={
|
||||||
|
snapshotDetails.isManagedRepository
|
||||||
|
? i18n.translate(
|
||||||
|
'xpack.snapshotRestore.snapshotDetails.deleteManagedRepositorySnapshotButtonTitle',
|
||||||
|
{
|
||||||
|
defaultMessage:
|
||||||
|
'You cannot delete a snapshot stored in a managed repository.',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="xpack.snapshotRestore.snapshotDetails.deleteButtonLabel"
|
id="xpack.snapshotRestore.snapshotDetails.deleteButtonLabel"
|
||||||
|
|
|
@ -188,14 +188,22 @@ export const SnapshotTable: React.FunctionComponent<Props> = ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
render: ({ snapshot, repository }: SnapshotDetails) => {
|
render: ({ snapshot, repository, isManagedRepository }: SnapshotDetails) => {
|
||||||
return (
|
return (
|
||||||
<SnapshotDeleteProvider>
|
<SnapshotDeleteProvider>
|
||||||
{deleteSnapshotPrompt => {
|
{deleteSnapshotPrompt => {
|
||||||
const label = i18n.translate(
|
const label = !isManagedRepository
|
||||||
'xpack.snapshotRestore.snapshotList.table.actionDeleteTooltip',
|
? i18n.translate(
|
||||||
{ defaultMessage: 'Delete' }
|
'xpack.snapshotRestore.snapshotList.table.actionDeleteTooltip',
|
||||||
);
|
{ defaultMessage: 'Delete' }
|
||||||
|
)
|
||||||
|
: i18n.translate(
|
||||||
|
'xpack.snapshotRestore.snapshotList.table.deleteManagedRepositorySnapshotTooltip',
|
||||||
|
{
|
||||||
|
defaultMessage:
|
||||||
|
'You cannot delete a snapshot stored in a managed repository.',
|
||||||
|
}
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<EuiToolTip content={label}>
|
<EuiToolTip content={label}>
|
||||||
<EuiButtonIcon
|
<EuiButtonIcon
|
||||||
|
@ -212,6 +220,7 @@ export const SnapshotTable: React.FunctionComponent<Props> = ({
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
deleteSnapshotPrompt([{ snapshot, repository }], onSnapshotDeleted)
|
deleteSnapshotPrompt([{ snapshot, repository }], onSnapshotDeleted)
|
||||||
}
|
}
|
||||||
|
isDisabled={isManagedRepository}
|
||||||
/>
|
/>
|
||||||
</EuiToolTip>
|
</EuiToolTip>
|
||||||
);
|
);
|
||||||
|
@ -248,6 +257,17 @@ export const SnapshotTable: React.FunctionComponent<Props> = ({
|
||||||
|
|
||||||
const selection = {
|
const selection = {
|
||||||
onSelectionChange: (newSelectedItems: SnapshotDetails[]) => setSelectedItems(newSelectedItems),
|
onSelectionChange: (newSelectedItems: SnapshotDetails[]) => setSelectedItems(newSelectedItems),
|
||||||
|
selectable: ({ isManagedRepository }: SnapshotDetails) => !isManagedRepository,
|
||||||
|
selectableMessage: (selectable: boolean) => {
|
||||||
|
if (!selectable) {
|
||||||
|
return i18n.translate(
|
||||||
|
'xpack.snapshotRestore.snapshotList.table.deleteManagedRepositorySnapshotTooltip',
|
||||||
|
{
|
||||||
|
defaultMessage: 'You cannot delete a snapshot stored in a managed repository.',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = {
|
const search = {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Cloud has its own system for managing snapshots and we want to make
|
||||||
|
// this clear when Snapshot and Restore is used in a Cloud deployment.
|
||||||
|
// Retrieve the Cloud-managed repository name so that UI can switch
|
||||||
|
// logical paths based on this information.
|
||||||
|
export const getManagedRepositoryName = async (
|
||||||
|
callWithInternalUser: any
|
||||||
|
): Promise<string | undefined> => {
|
||||||
|
try {
|
||||||
|
const { persistent, transient, defaults } = await callWithInternalUser('cluster.getSettings', {
|
||||||
|
filterPath: '*.*managed_repository',
|
||||||
|
flatSettings: true,
|
||||||
|
includeDefaults: true,
|
||||||
|
});
|
||||||
|
const { 'cluster.metadata.managed_repository': managedRepositoryName = undefined } = {
|
||||||
|
...defaults,
|
||||||
|
...persistent,
|
||||||
|
...transient,
|
||||||
|
};
|
||||||
|
return managedRepositoryName;
|
||||||
|
} catch (e) {
|
||||||
|
// Silently swallow error and return undefined for managed repository name
|
||||||
|
// so that downstream calls are not blocked. In a healthy environment we do
|
||||||
|
// not expect to reach here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
|
@ -11,3 +11,4 @@ export {
|
||||||
export { cleanSettings } from './clean_settings';
|
export { cleanSettings } from './clean_settings';
|
||||||
export { deserializeSnapshotDetails } from './snapshot_serialization';
|
export { deserializeSnapshotDetails } from './snapshot_serialization';
|
||||||
export { deserializeRestoreShard } from './restore_serialization';
|
export { deserializeRestoreShard } from './restore_serialization';
|
||||||
|
export { getManagedRepositoryName } from './get_managed_repository_name';
|
||||||
|
|
|
@ -91,6 +91,7 @@ describe('deserializeSnapshotDetails', () => {
|
||||||
failed: 1,
|
failed: 1,
|
||||||
successful: 2,
|
successful: 2,
|
||||||
},
|
},
|
||||||
|
isManagedRepository: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,8 @@ import { SnapshotDetailsEs } from '../types';
|
||||||
|
|
||||||
export function deserializeSnapshotDetails(
|
export function deserializeSnapshotDetails(
|
||||||
repository: string,
|
repository: string,
|
||||||
snapshotDetailsEs: SnapshotDetailsEs
|
snapshotDetailsEs: SnapshotDetailsEs,
|
||||||
|
managedRepository?: string
|
||||||
): SnapshotDetails {
|
): SnapshotDetails {
|
||||||
if (!snapshotDetailsEs || typeof snapshotDetailsEs !== 'object') {
|
if (!snapshotDetailsEs || typeof snapshotDetailsEs !== 'object') {
|
||||||
throw new Error('Unable to deserialize snapshot details');
|
throw new Error('Unable to deserialize snapshot details');
|
||||||
|
@ -75,5 +76,6 @@ export function deserializeSnapshotDetails(
|
||||||
durationInMillis,
|
durationInMillis,
|
||||||
indexFailures,
|
indexFailures,
|
||||||
shards,
|
shards,
|
||||||
|
isManagedRepository: repository === managedRepository,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,6 @@ import { registerRestoreRoutes } from './restore';
|
||||||
export const registerRoutes = (router: Router, plugins: Plugins): void => {
|
export const registerRoutes = (router: Router, plugins: Plugins): void => {
|
||||||
registerAppRoutes(router, plugins);
|
registerAppRoutes(router, plugins);
|
||||||
registerRepositoriesRoutes(router, plugins);
|
registerRepositoriesRoutes(router, plugins);
|
||||||
registerSnapshotsRoutes(router);
|
registerSnapshotsRoutes(router, plugins);
|
||||||
registerRestoreRoutes(router);
|
registerRestoreRoutes(router);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,11 @@ import { DEFAULT_REPOSITORY_TYPES, REPOSITORY_PLUGINS_MAP } from '../../../commo
|
||||||
import { Repository, RepositoryType, RepositoryVerification } from '../../../common/types';
|
import { Repository, RepositoryType, RepositoryVerification } from '../../../common/types';
|
||||||
|
|
||||||
import { Plugins } from '../../../shim';
|
import { Plugins } from '../../../shim';
|
||||||
import { deserializeRepositorySettings, serializeRepositorySettings } from '../../lib';
|
import {
|
||||||
|
deserializeRepositorySettings,
|
||||||
|
serializeRepositorySettings,
|
||||||
|
getManagedRepositoryName,
|
||||||
|
} from '../../lib';
|
||||||
|
|
||||||
let isCloudEnabled: boolean = false;
|
let isCloudEnabled: boolean = false;
|
||||||
let callWithInternalUser: any;
|
let callWithInternalUser: any;
|
||||||
|
@ -30,20 +34,6 @@ export function registerRepositoriesRoutes(router: Router, plugins: Plugins) {
|
||||||
router.delete('repositories/{names}', deleteHandler);
|
router.delete('repositories/{names}', deleteHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getManagedRepositoryName = async () => {
|
|
||||||
const { persistent, transient, defaults } = await callWithInternalUser('cluster.getSettings', {
|
|
||||||
filterPath: '*.*managed_repository',
|
|
||||||
flatSettings: true,
|
|
||||||
includeDefaults: true,
|
|
||||||
});
|
|
||||||
const { 'cluster.metadata.managed_repository': managedRepositoryName = undefined } = {
|
|
||||||
...defaults,
|
|
||||||
...persistent,
|
|
||||||
...transient,
|
|
||||||
};
|
|
||||||
return managedRepositoryName;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllHandler: RouterRouteHandler = async (
|
export const getAllHandler: RouterRouteHandler = async (
|
||||||
req,
|
req,
|
||||||
callWithRequest
|
callWithRequest
|
||||||
|
@ -51,7 +41,7 @@ export const getAllHandler: RouterRouteHandler = async (
|
||||||
repositories: Repository[];
|
repositories: Repository[];
|
||||||
managedRepository?: string;
|
managedRepository?: string;
|
||||||
}> => {
|
}> => {
|
||||||
const managedRepository = await getManagedRepositoryName();
|
const managedRepository = await getManagedRepositoryName(callWithInternalUser);
|
||||||
const repositoriesByName = await callWithRequest('snapshot.getRepository', {
|
const repositoriesByName = await callWithRequest('snapshot.getRepository', {
|
||||||
repository: '_all',
|
repository: '_all',
|
||||||
});
|
});
|
||||||
|
@ -76,7 +66,7 @@ export const getOneHandler: RouterRouteHandler = async (
|
||||||
snapshots: { count: number | null } | {};
|
snapshots: { count: number | null } | {};
|
||||||
}> => {
|
}> => {
|
||||||
const { name } = req.params;
|
const { name } = req.params;
|
||||||
const managedRepository = await getManagedRepositoryName();
|
const managedRepository = await getManagedRepositoryName(callWithInternalUser);
|
||||||
const repositoryByName = await callWithRequest('snapshot.getRepository', { repository: name });
|
const repositoryByName = await callWithRequest('snapshot.getRepository', { repository: name });
|
||||||
const {
|
const {
|
||||||
responses: snapshotResponses,
|
responses: snapshotResponses,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Request, ResponseToolkit } from 'hapi';
|
import { Request, ResponseToolkit } from 'hapi';
|
||||||
import { getAllHandler, getOneHandler, deleteHandler } from './snapshots';
|
import { registerSnapshotsRoutes, getAllHandler, getOneHandler, deleteHandler } from './snapshots';
|
||||||
|
|
||||||
const defaultSnapshot = {
|
const defaultSnapshot = {
|
||||||
repository: undefined,
|
repository: undefined,
|
||||||
|
@ -27,6 +27,29 @@ const defaultSnapshot = {
|
||||||
|
|
||||||
describe('[Snapshot and Restore API Routes] Snapshots', () => {
|
describe('[Snapshot and Restore API Routes] Snapshots', () => {
|
||||||
const mockResponseToolkit = {} as ResponseToolkit;
|
const mockResponseToolkit = {} as ResponseToolkit;
|
||||||
|
const mockCallWithInternalUser = jest.fn().mockReturnValue({
|
||||||
|
persistent: {
|
||||||
|
'cluster.metadata.managed_repository': 'found-snapshots',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSnapshotsRoutes(
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
get: () => {},
|
||||||
|
// @ts-ignore
|
||||||
|
post: () => {},
|
||||||
|
// @ts-ignore
|
||||||
|
put: () => {},
|
||||||
|
// @ts-ignore
|
||||||
|
delete: () => {},
|
||||||
|
// @ts-ignore
|
||||||
|
patch: () => {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
elasticsearch: { getCluster: () => ({ callWithInternalUser: mockCallWithInternalUser }) },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
describe('getAllHandler()', () => {
|
describe('getAllHandler()', () => {
|
||||||
const mockRequest = {} as Request;
|
const mockRequest = {} as Request;
|
||||||
|
@ -65,8 +88,18 @@ describe('[Snapshot and Restore API Routes] Snapshots', () => {
|
||||||
errors: {},
|
errors: {},
|
||||||
repositories: ['fooRepository', 'barRepository'],
|
repositories: ['fooRepository', 'barRepository'],
|
||||||
snapshots: [
|
snapshots: [
|
||||||
{ ...defaultSnapshot, repository: 'fooRepository', snapshot: 'snapshot1' },
|
{
|
||||||
{ ...defaultSnapshot, repository: 'barRepository', snapshot: 'snapshot2' },
|
...defaultSnapshot,
|
||||||
|
repository: 'fooRepository',
|
||||||
|
snapshot: 'snapshot1',
|
||||||
|
isManagedRepository: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultSnapshot,
|
||||||
|
repository: 'barRepository',
|
||||||
|
snapshot: 'snapshot2',
|
||||||
|
isManagedRepository: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,7 +110,11 @@ describe('[Snapshot and Restore API Routes] Snapshots', () => {
|
||||||
test('returns empty arrays if no snapshots returned from ES', async () => {
|
test('returns empty arrays if no snapshots returned from ES', async () => {
|
||||||
const mockSnapshotGetRepositoryEsResponse = {};
|
const mockSnapshotGetRepositoryEsResponse = {};
|
||||||
const callWithRequest = jest.fn().mockReturnValue(mockSnapshotGetRepositoryEsResponse);
|
const callWithRequest = jest.fn().mockReturnValue(mockSnapshotGetRepositoryEsResponse);
|
||||||
const expectedResponse = { errors: [], snapshots: [], repositories: [] };
|
const expectedResponse = {
|
||||||
|
errors: [],
|
||||||
|
snapshots: [],
|
||||||
|
repositories: [],
|
||||||
|
};
|
||||||
|
|
||||||
const response = await getAllHandler(mockRequest, callWithRequest, mockResponseToolkit);
|
const response = await getAllHandler(mockRequest, callWithRequest, mockResponseToolkit);
|
||||||
expect(response).toEqual(expectedResponse);
|
expect(response).toEqual(expectedResponse);
|
||||||
|
@ -119,6 +156,7 @@ describe('[Snapshot and Restore API Routes] Snapshots', () => {
|
||||||
...defaultSnapshot,
|
...defaultSnapshot,
|
||||||
snapshot,
|
snapshot,
|
||||||
repository,
|
repository,
|
||||||
|
isManagedRepository: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await getOneHandler(mockOneRequest, callWithRequest, mockResponseToolkit);
|
const response = await getOneHandler(mockOneRequest, callWithRequest, mockResponseToolkit);
|
||||||
|
|
|
@ -9,10 +9,14 @@ import {
|
||||||
wrapCustomError,
|
wrapCustomError,
|
||||||
} from '../../../../../server/lib/create_router/error_wrappers';
|
} from '../../../../../server/lib/create_router/error_wrappers';
|
||||||
import { SnapshotDetails } from '../../../common/types';
|
import { SnapshotDetails } from '../../../common/types';
|
||||||
import { deserializeSnapshotDetails } from '../../lib';
|
import { Plugins } from '../../../shim';
|
||||||
|
import { deserializeSnapshotDetails, getManagedRepositoryName } from '../../lib';
|
||||||
import { SnapshotDetailsEs } from '../../types';
|
import { SnapshotDetailsEs } from '../../types';
|
||||||
|
|
||||||
export function registerSnapshotsRoutes(router: Router) {
|
let callWithInternalUser: any;
|
||||||
|
|
||||||
|
export function registerSnapshotsRoutes(router: Router, plugins: Plugins) {
|
||||||
|
callWithInternalUser = plugins.elasticsearch.getCluster('data').callWithInternalUser;
|
||||||
router.get('snapshots', getAllHandler);
|
router.get('snapshots', getAllHandler);
|
||||||
router.get('snapshots/{repository}/{snapshot}', getOneHandler);
|
router.get('snapshots/{repository}/{snapshot}', getOneHandler);
|
||||||
router.delete('snapshots/{ids}', deleteHandler);
|
router.delete('snapshots/{ids}', deleteHandler);
|
||||||
|
@ -25,7 +29,10 @@ export const getAllHandler: RouterRouteHandler = async (
|
||||||
snapshots: SnapshotDetails[];
|
snapshots: SnapshotDetails[];
|
||||||
errors: any[];
|
errors: any[];
|
||||||
repositories: string[];
|
repositories: string[];
|
||||||
|
managedRepository?: string;
|
||||||
}> => {
|
}> => {
|
||||||
|
const managedRepository = await getManagedRepositoryName(callWithInternalUser);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: For 8.0, replace the logic in this handler with one call to `GET /_snapshot/_all/_all`
|
* TODO: For 8.0, replace the logic in this handler with one call to `GET /_snapshot/_all/_all`
|
||||||
* when no repositories bug is fixed: https://github.com/elastic/elasticsearch/issues/43547
|
* when no repositories bug is fixed: https://github.com/elastic/elasticsearch/issues/43547
|
||||||
|
@ -64,7 +71,7 @@ export const getAllHandler: RouterRouteHandler = async (
|
||||||
// Decorate each snapshot with the repository with which it's associated.
|
// Decorate each snapshot with the repository with which it's associated.
|
||||||
fetchedResponses.forEach(({ snapshots: fetchedSnapshots }) => {
|
fetchedResponses.forEach(({ snapshots: fetchedSnapshots }) => {
|
||||||
fetchedSnapshots.forEach(snapshot => {
|
fetchedSnapshots.forEach(snapshot => {
|
||||||
snapshots.push(deserializeSnapshotDetails(repository, snapshot));
|
snapshots.push(deserializeSnapshotDetails(repository, snapshot, managedRepository));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,6 +97,7 @@ export const getOneHandler: RouterRouteHandler = async (
|
||||||
callWithRequest
|
callWithRequest
|
||||||
): Promise<SnapshotDetails> => {
|
): Promise<SnapshotDetails> => {
|
||||||
const { repository, snapshot } = req.params;
|
const { repository, snapshot } = req.params;
|
||||||
|
const managedRepository = await getManagedRepositoryName(callWithInternalUser);
|
||||||
const {
|
const {
|
||||||
responses: snapshotResponses,
|
responses: snapshotResponses,
|
||||||
}: {
|
}: {
|
||||||
|
@ -104,7 +112,11 @@ export const getOneHandler: RouterRouteHandler = async (
|
||||||
});
|
});
|
||||||
|
|
||||||
if (snapshotResponses && snapshotResponses[0] && snapshotResponses[0].snapshots) {
|
if (snapshotResponses && snapshotResponses[0] && snapshotResponses[0].snapshots) {
|
||||||
return deserializeSnapshotDetails(repository, snapshotResponses[0].snapshots[0]);
|
return deserializeSnapshotDetails(
|
||||||
|
repository,
|
||||||
|
snapshotResponses[0].snapshots[0],
|
||||||
|
managedRepository
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If snapshot doesn't exist, ES will return 200 with an error object, so manually throw 404 here
|
// If snapshot doesn't exist, ES will return 200 with an error object, so manually throw 404 here
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue