Skip to content

Commit

Permalink
Add Toasts to Observability Dashboards (opensearch-project#435)
Browse files Browse the repository at this point in the history
* Fixes
* Panel View (legacy)
  - Duplicate
  - Rename

Signed-off-by: Peter Fitzgibbons <[email protected]>

* Toasts use hook from useOpenSearchDashboards context provider

Signed-off-by: Peter Fitzgibbons <[email protected]>

* Testing for CustomPanel Toast

Signed-off-by: Peter Fitzgibbons <[email protected]>

* update catches from comments, minor code cleaning

Signed-off-by: Shenoy Pratik <[email protected]>

* update tests

Signed-off-by: Shenoy Pratik <[email protected]>

* remove unused redux slice

Signed-off-by: Shenoy Pratik <[email protected]>

* revert cypress changes

Signed-off-by: Shenoy Pratik <[email protected]>

* add toasts to SOflyout

Signed-off-by: Shenoy Pratik <[email protected]>

* fix messaging for multiple delete

Signed-off-by: Derek Ho <[email protected]>

* fix up toast and error handling for create and delete flows

Signed-off-by: Derek Ho <[email protected]>

* fix up clone

Signed-off-by: Derek Ho <[email protected]>

* fix rename in table

Signed-off-by: Derek Ho <[email protected]>

* fix rename in custom panel so view

Signed-off-by: Derek Ho <[email protected]>

* fix up panel toasts

Signed-off-by: Derek Ho <[email protected]>

* fix up for flyout

Signed-off-by: Derek Ho <[email protected]>

* code cleanup

Signed-off-by: Derek Ho <[email protected]>

* finish merge

Signed-off-by: Derek Ho <[email protected]>

* fix up PR comments

Signed-off-by: Derek Ho <[email protected]>

---------

Signed-off-by: Peter Fitzgibbons <[email protected]>
Signed-off-by: Shenoy Pratik <[email protected]>
Signed-off-by: Derek Ho <[email protected]>
Co-authored-by: Peter Fitzgibbons <[email protected]>
Co-authored-by: Derek Ho <[email protected]>
(cherry picked from commit a9d1d37)
Signed-off-by: Derek Ho <[email protected]>
  • Loading branch information
3 people committed May 8, 2023
1 parent 3340703 commit 2f4ba15
Show file tree
Hide file tree
Showing 14 changed files with 1,990 additions and 24 deletions.
165 changes: 165 additions & 0 deletions .cypress/integration/3_panels.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,168 @@ describe('Clean up all test data', () => {
cy.get('.euiTextAlign').contains('No Operational Panels').should('exist');
});
});

const moveToOsdDashboards = () => {
cy.visit(`${Cypress.env('opensearchDashboards')}/app/dashboards#/`);
cy.wait(delay * 3);
};

const moveToEventsHome = () => {
cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-logs#/`);
cy.wait(6000);
};

const moveToPanelHome = () => {
cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/`, {
timeout: 3000,
});
cy.wait(delay * 3);
};

const testPanelTableCell = (name = TEST_PANEL) => cy.get('.euiTableCellContent').contains(name);

const moveToTestPanel = () => {
moveToPanelHome();
testPanelTableCell().trigger('mouseover').click();
cy.wait(delay * 3);
cy.get('h1').contains(TEST_PANEL).should('exist');
};

const TEST_PANEL_RX = new RegExp(TEST_PANEL + '.*');

const eraseLegacyPanels = () => {
cy.request({
method: 'GET',
failOnStatusCode: false,
url: 'api/observability/operational_panels/panels',
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
}).then((response) => {
response.body.panels.map((panel) => {
cy.request({
method: 'DELETE',
failOnStatusCode: false,
url: `api/observability/operational_panels/panels/${panel.id}`,
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
}).then((response) => {
const deletedId = response.allRequestResponses[0]['Request URL'].split('/').slice(-1);
console.log('erased panel', deletedId);
});
});
});
};

const eraseSavedObjectPaenls = () => {
return cy
.request({
method: 'get',
failOnStatusCode: false,
url: 'api/saved_objects/_find?type=observability-panel',
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
})
.then((response) => {
response.body.saved_objects.map((soPanel) => {
cy.request({
method: 'DELETE',
failOnStatusCode: false,
url: `api/saved_objects/observability-panel/${soPanel.id}`,
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
});
});
});
};

const eraseTestPanels = () => {
eraseLegacyPanels();
eraseSavedObjectPaenls();
};
const uuidRx = /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/;

const clickCreatePanelButton = () =>
cy.get('a[data-test-subj="customPanels__createNewPanels"]').click();

const createSavedObjectPanel = (newName = TEST_PANEL) => {
const result = cy
.request({
method: 'POST',
failOnStatusCode: false,
url: 'api/saved_objects/observability-panel',
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
body: {
attributes: {
title: newName,
description: '',
dateCreated: 1681127334085,
dateModified: 1681127334085,
timeRange: {
to: 'now',
from: 'now-1d',
},
queryFilter: {
query: '',
language: 'ppl',
},
visualizations: [],
applicationId: '',
},
},
})
.then((response) => console.log(response));
};

const createLegacyPanel = (newName = TEST_PANEL) => {
const result = cy.request({
method: 'POST',
failOnStatusCode: false,
url: 'api/observability/operational_panels/panels',
headers: {
'content-type': 'application/json;charset=UTF-8',
'osd-xsrf': true,
},
body: { panelName: newName },
});
};

const expectUuid = (anchorElem) => {
anchorElem.invoke('attr', 'href').should('match', uuidRx);
};

const expectLegacyId = (anchorElem) => {
anchorElem.invoke('attr', 'href').should('not.match', uuidRx);
};

const clickDeleteAction = () => {
cy.get('button[data-test-subj="deleteContextMenuItem"]').click();
};

const openActionsDropdown = () => {
cy.get('button[data-test-subj="operationalPanelsActionsButton"]').click();
};

const selectThePanel = () => {
cy.get('.euiCheckbox__input[title="Select this row"]').then(() => {
cy.get('.euiCheckbox__input[title="Select this row"]').check({ force: true });
});
};

const expectToastWith = (title) => {
cy.get('.euiToastHeader__title').contains(title).should('exist');
};

const confirmModal = () => {
cy.get('button[data-test-subj="runModalButton"]').click();
};
9 changes: 7 additions & 2 deletions common/constants/custom_panels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@
*/

export const CUSTOM_PANELS_API_PREFIX = '/api/observability/operational_panels';
export const CUSTOM_PANELS_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/observability-plugin/operational-panels/';
export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this custom panel.';
export const CUSTOM_PANELS_DOCUMENTATION_URL =
'https://opensearch.org/docs/latest/observability-plugin/operational-panels/';
export const CREATE_PANEL_MESSAGE = 'Enter a name to describe the purpose of this Observability Dashboard.';

export const CUSTOM_PANELS_SAVED_OBJECT_TYPE = 'observability-panel';

export const CUSTOM_PANEL_SLICE = 'customPanel';
2 changes: 1 addition & 1 deletion common/constants/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const DSL_ENDPOINT = '/_plugins/_dsl';

export const observabilityID = 'observability-dashboards';
export const observabilityTitle = 'Observability';
export const observabilityPluginOrder = 6000;
export const observabilityPluginOrder = 1500;

// Shared Constants
export const SQL_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/search-plugins/sql/index/';
Expand Down
37 changes: 37 additions & 0 deletions public/components/common/toast/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { ToastInputFields } from '../../../../../../src/core/public';
import { coreRefs } from '../../../framework/core_refs';

type Color = 'success' | 'primary' | 'warning' | 'danger' | undefined;

export const useToast = () => {
const toasts = coreRefs.toasts!;

const setToast = (title: string, color: Color = 'success', text?: string) => {
const newToast: ToastInputFields = {
id: new Date().toISOString(),
title,
text,
};
switch (color) {
case 'danger': {
toasts.addDanger(newToast);
break;
}
case 'warning': {
toasts.addWarning(newToast);
break;
}
default: {
toasts.addSuccess(newToast);
break;
}
}
};

return { setToast };
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ import PPLService from '../../../../public/services/requests/ppl';
import DSLService from '../../../../public/services/requests/dsl';
import { coreStartMock } from '../../../../test/__mocks__/coreMocks';
import { HttpResponse } from '../../../../../../src/core/public';
import { applyMiddleware, createStore } from 'redux';
import { rootReducer } from '../../../framework/redux/reducers';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';

describe('Panels View Component', () => {
configure({ adapter: new Adapter() });

const store = createStore(rootReducer, applyMiddleware(thunk));

it('renders panel view container without visualizations', async () => {
httpClientMock.get = jest.fn(() =>
Promise.resolve((sampleEmptyPanel as unknown) as HttpResponse)
Expand Down
Loading

0 comments on commit 2f4ba15

Please sign in to comment.