Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export type TestSubjects =
| 'createTemplateButton'
| 'dataStreamsEmptyPromptTemplateLink'
| 'dataStreamTable'
| 'deleteDataStreamsButton'
| 'deleteDataStreamButton'
| 'deleteSystemTemplateCallOut'
| 'deleteTemplateButton'
| 'deleteTemplatesConfirmation'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
clickNameAt: (index: number) => void;
clickIndicesAt: (index: number) => void;
clickDeleteActionAt: (index: number) => void;
selectDataStream: (name: string, selected: boolean) => void;
clickConfirmDelete: () => void;
clickDeleteDataStreamButton: () => void;
clickDetailPanelIndexTemplateLink: () => void;
Expand Down Expand Up @@ -125,6 +126,13 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
findDeleteActionAt(index).simulate('click');
};

const selectDataStream = (name: string, selected: boolean) => {
const {
form: { selectCheckBox },
} = testBed;
selectCheckBox(`checkboxSelectRow-${name}`, selected);
};

const findDeleteConfirmationModal = () => {
const { find } = testBed;
return find('deleteDataStreamsConfirmation');
Expand Down Expand Up @@ -194,6 +202,7 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
clickNameAt,
clickIndicesAt,
clickDeleteActionAt,
selectDataStream,
clickConfirmDelete,
clickDeleteDataStreamButton,
clickDetailPanelIndexTemplateLink,
Expand Down Expand Up @@ -223,6 +232,9 @@ export const createDataStreamPayload = (dataStream: Partial<DataStream>): DataSt
indexTemplateName: 'indexTemplate',
storageSize: '1b',
maxTimeStamp: 420,
privileges: {
delete_index: true,
},
...dataStream,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,77 @@ describe('Data Streams tab', () => {
expect(tableCellsValues).toEqual([['', 'non-managed-data-stream', 'green', '1', 'Delete']]);
});
});

describe('data stream privileges', () => {
describe('delete', () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamWithDelete = createDataStreamPayload({
name: 'dataStreamWithDelete',
privileges: { delete_index: true },
});
const dataStreamNoDelete = createDataStreamPayload({
name: 'dataStreamNoDelete',
privileges: { delete_index: false },
});

beforeEach(async () => {
setLoadDataStreamsResponse([dataStreamWithDelete, dataStreamNoDelete]);

testBed = await setup({ history: createMemoryHistory() });
await act(async () => {
testBed.actions.goToDataStreamsList();
});
testBed.component.update();
});

test('displays/hides delete button depending on data streams privileges', async () => {
const { table } = testBed;
const { tableCellsValues } = table.getMetaData('dataStreamTable');

expect(tableCellsValues).toEqual([
['', 'dataStreamNoDelete', 'green', '1', ''],
['', 'dataStreamWithDelete', 'green', '1', 'Delete'],
]);
});

test('displays/hides delete action depending on data streams privileges', async () => {
const {
actions: { selectDataStream },
find,
} = testBed;

selectDataStream('dataStreamNoDelete', true);
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamWithDelete', true);
expect(find('deleteDataStreamsButton').exists()).toBeFalsy();

selectDataStream('dataStreamNoDelete', false);
expect(find('deleteDataStreamsButton').exists()).toBeTruthy();
});

test('displays delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamWithDelete);
await clickNameAt(1);

expect(find('deleteDataStreamButton').exists()).toBeTruthy();
});

test('hides delete button in detail panel', async () => {
const {
actions: { clickNameAt },
find,
} = testBed;
setLoadDataStreamResponse(dataStreamNoDelete);
await clickNameAt(0);

expect(find('deleteDataStreamButton').exists()).toBeFalsy();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
store_size: storageSize,
maximum_timestamp: maxTimeStamp,
_meta,
privileges,
} = dataStreamFromEs;

return {
Expand All @@ -37,6 +38,7 @@ export function deserializeDataStream(dataStreamFromEs: DataStreamFromEs): DataS
storageSize,
maxTimeStamp,
_meta,
privileges,
};
}

Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/index_management/common/types/data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ interface MetaFieldFromEs {
managed: boolean;
}

interface PrivilegesFieldFromEs {
delete_index: boolean;
}

type PrivilegesField = PrivilegesFieldFromEs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand why we create an alias here. Do you mind explaining?

Also not sure why there is a "Field" in the naming :) I would have called the interface simply Privileges instead of PrivilegesFieldFromEs but maybe I'm missing something.


type MetaField = MetaFieldFromEs;

export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED';
Expand All @@ -31,6 +37,7 @@ export interface DataStreamFromEs {
ilm_policy?: string;
store_size?: string;
maximum_timestamp?: number;
privileges: PrivilegesFieldFromEs;
}

export interface DataStreamIndexFromEs {
Expand All @@ -51,6 +58,7 @@ export interface DataStream {
storageSize?: string;
maxTimeStamp?: number;
_meta?: MetaField;
privileges: PrivilegesField;
}

export interface DataStreamIndex {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export const DataStreamDetailPanel: React.FunctionComponent<Props> = ({
</EuiButtonEmpty>
</EuiFlexItem>

{!isLoading && !error ? (
{!isLoading && !error && dataStream?.privileges.delete_index ? (
<EuiFlexItem grow={false}>
<EuiButton
color="danger"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
history,
}) => {
const { isDeepLink } = extractQueryParams(search);
const decodedDataStreamName = attemptToURIDecode(dataStreamName);

const {
core: { getUrlForApp },
Expand Down Expand Up @@ -241,8 +242,8 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa

<DataStreamTable
filters={
isDeepLink && dataStreamName !== undefined
? `name="${attemptToURIDecode(dataStreamName)}"`
isDeepLink && decodedDataStreamName !== undefined
? `name="${decodedDataStreamName}"`
: ''
}
dataStreams={filteredDataStreams}
Expand All @@ -262,9 +263,9 @@ export const DataStreamList: React.FunctionComponent<RouteComponentProps<MatchPa
If the user has been deep-linked, they'll expect to see the detail panel because it reflects
the URL state, even if there are no data streams or if there was an error loading them.
*/}
{dataStreamName && (
{decodedDataStreamName && (
<DataStreamDetailPanel
dataStreamName={attemptToURIDecode(dataStreamName)!}
dataStreamName={decodedDataStreamName}
onClose={(shouldReload?: boolean) => {
history.push(`/${Section.DataStreams}`);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
},
isPrimary: true,
'data-test-subj': 'deleteDataStream',
available: ({ privileges: { delete_index: deleteIndex } }: DataStream) => deleteIndex,
},
],
});
Expand All @@ -188,9 +189,10 @@ export const DataStreamTable: React.FunctionComponent<Props> = ({
incremental: true,
},
toolsLeft:
selection.length > 0 ? (
selection.length > 0 &&
selection.every((dataStream: DataStream) => dataStream.privileges.delete_index) ? (
<EuiButton
data-test-subj="deletDataStreamsButton"
data-test-subj="deleteDataStreamsButton"
onClick={() => setDataStreamsToDelete(selection.map(({ name }: DataStream) => name))}
color="danger"
>
Expand Down
22 changes: 0 additions & 22 deletions x-pack/plugins/index_management/server/client/elasticsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,6 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any)
const dataManagement = Client.prototype.dataManagement.prototype;

// Data streams
dataManagement.getDataStreams = ca({
urls: [
{
fmt: '/_data_stream',
},
],
method: 'GET',
});

dataManagement.getDataStream = ca({
urls: [
{
fmt: '/_data_stream/<%=name%>',
req: {
name: {
type: 'string',
},
},
},
],
method: 'GET',
});

// We don't allow the user to create a data stream in the UI or API. We're just adding this here
// to enable the API integration tests.
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/index_management/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { PLUGIN } from '../common';
import { Dependencies } from './types';
import { ApiRoutes } from './routes';
import { License, IndexDataEnricher } from './services';
import { isEsError } from './shared_imports';
import { isEsError, handleEsError } from './shared_imports';
import { elasticsearchJsPlugin } from './client/elasticsearch';

export interface DataManagementContext {
Expand Down Expand Up @@ -110,6 +110,7 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
indexDataEnricher: this.indexDataEnricher,
lib: {
isEsError,
handleEsError,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('GET privileges', () => {
indexDataEnricher: mockedIndexDataEnricher,
lib: {
isEsError: jest.fn(),
handleEsError: jest.fn(),
},
});

Expand Down Expand Up @@ -123,6 +124,7 @@ describe('GET privileges', () => {
indexDataEnricher: mockedIndexDataEnricher,
lib: {
isEsError: jest.fn(),
handleEsError: jest.fn(),
},
});

Expand Down
Loading