Skip to content

Commit

Permalink
Added data streams privileges to better control delete actions in UI (e…
Browse files Browse the repository at this point in the history
…lastic#83573) (elastic#84428)

* Added data streams privileges to better control delete actions in UI

* Fix type check issues

* Change data streams privileges request

* Fixed type check issue

* Fixed api integration test

* Cleaned up not needed code

* Renamed some data streams and added a default value for stats find

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
yuliacech and kibanamachine authored Nov 30, 2020
1 parent ee87f19 commit f2833fc
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 113 deletions.
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
16 changes: 12 additions & 4 deletions x-pack/plugins/index_management/common/types/data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ interface TimestampFieldFromEs {

type TimestampField = TimestampFieldFromEs;

interface MetaFieldFromEs {
interface MetaFromEs {
managed_by: string;
package: any;
managed: boolean;
}

type MetaField = MetaFieldFromEs;
type Meta = MetaFromEs;

interface PrivilegesFromEs {
delete_index: boolean;
}

type Privileges = PrivilegesFromEs;

export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED';

Expand All @@ -25,12 +31,13 @@ export interface DataStreamFromEs {
timestamp_field: TimestampFieldFromEs;
indices: DataStreamIndexFromEs[];
generation: number;
_meta?: MetaFieldFromEs;
_meta?: MetaFromEs;
status: HealthFromEs;
template: string;
ilm_policy?: string;
store_size?: string;
maximum_timestamp?: number;
privileges: PrivilegesFromEs;
}

export interface DataStreamIndexFromEs {
Expand All @@ -50,7 +57,8 @@ export interface DataStream {
ilmPolicyName?: string;
storageSize?: string;
maxTimeStamp?: number;
_meta?: MetaField;
_meta?: Meta;
privileges: Privileges;
}

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

0 comments on commit f2833fc

Please sign in to comment.