Skip to content

Commit

Permalink
[Feature] Saved object refactoring for panels (#355)
Browse files Browse the repository at this point in the history
* Operational Panels Integrate with Dashboards-List

---------

Signed-off-by: Peter Fitzgibbons <pjfitz@amazon.com>
Signed-off-by: Derek Ho <dxho@amazon.com>
Signed-off-by: Eric Wei <menwe@amazon.com>
Co-authored-by: Peter Fitzgibbons <pjfitz@amazon.com>
Co-authored-by: Derek Ho <dxho@amazon.com>
  • Loading branch information
3 people authored Apr 14, 2023
1 parent 583010d commit 775a77e
Show file tree
Hide file tree
Showing 28 changed files with 2,510 additions and 271 deletions.
Binary file added .cypress/.DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,6 @@ import {

import { suppressResizeObserverIssue } from '../utils/constants';

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

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

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

describe('Adding sample data and visualization', () => {
it('Adds sample flights data for visualization paragraph', () => {
Expand Down Expand Up @@ -96,44 +78,78 @@ describe('Creating visualizations', () => {
});
});

describe('Testing panels table', () => {
describe.only('Testing panels table', () => {
beforeEach(() => {
moveToPanelHome();
eraseTestPanels();
});

it('Displays error toast for invalid panel name', () => {
cy.get('button[data-test-subj="customPanels__createNewPanels"]').click();
cy.get('button[data-test-subj="runModalButton"]').click();
cy.get('.euiToastHeader__title').contains('Invalid Operational Panel name').should('exist');
clickCreatePanelButton();
confirmModal();
expectToastWith('Invalid Operational Panel name');
});

it('Creates a panel and redirects to the panel', () => {
cy.get('button[data-test-subj="customPanels__createNewPanels"]').click();
clickCreatePanelButton();
cy.get('input.euiFieldText').focus().type(TEST_PANEL, {
delay: 50,
});
cy.get('button[data-test-subj="runModalButton"]').click();
cy.contains(TEST_PANEL).should('exist');
});

it('Duplicates a panel', () => {
cy.get('.euiCheckbox__input[title="Select this row"]').eq(0).trigger('mouseover').click();
cy.get('button[data-test-subj="operationalPanelsActionsButton"]').click();
it('Duplicates a legacy panel', () => {
createLegacyPanel()
selectThePanel();
openActionsDropdown();
cy.get('button[data-test-subj="duplicateContextMenuItem"]').click();
cy.get('button[data-test-subj="runModalButton"]').click();
cy.contains(TEST_PANEL + " (copy)").should('exist');
const duplicate = testPanelTableCell()
expectUuid(duplicate)
});

it('Renames a panel', () => {
cy.get('.euiCheckbox__input[title="Select this row"]').eq(0).trigger('mouseover').click();
cy.get('button[data-test-subj="operationalPanelsActionsButton"]').click();
it.only('Duplicates a legacy panel', () => {
createLegacyPanel()
selectThePanel();
openActionsDropdown();
cy.get('button[data-test-subj="duplicateContextMenuItem"]').click();
cy.get('button[data-test-subj="runModalButton"]').click();
cy.contains(TEST_PANEL + " (copy)").should('exist');
const duplicate = testPanelTableCell()
expectUuid(duplicate)
});

it('Renames a saved-objects panel', () => {
createSavedObjectPanel()
cy.reload()

selectThePanel()
openActionsDropdown();
cy.get('button[data-test-subj="renameContextMenuItem"]').click();
cy.get('input.euiFieldText').focus().type(' (rename)', {
delay: 50,
});
cy.get('button[data-test-subj="runModalButton"]').click();
});

it('Renames a panel', () => {
createLegacyPanel();
cy.reload();
const cell = cy.get('.euiTableCellContent')
expectLegacyId(cell);
selectThePanel()
openActionsDropdown();
cy.get('button[data-test-subj="renameContextMenuItem"]').click();
cy.get('input.euiFieldText').focus().type(' (rename)');
cy.get('button[data-test-subj="runModalButton"]').click();
const renamed = testPanelTableCell()
expectUuid(renamed)
});

it('Searches existing panel', () => {
createLegacyPanel()
cy.get('input[data-test-subj="operationalPanelSearchBar"]')
.focus()
.type('this panel should not exist', {
Expand All @@ -154,9 +170,24 @@ describe('Testing panels table', () => {
.should('exist');
});

it('Deletes saved object panels', () => {
createSavedObjectPanel()
cy.get('input[data-test-subj="checkboxSelectAll"]').click();
openActionsDropdown();
cy.get('button[data-test-subj="deleteContextMenuItem"]').click();
cy.get('button[data-test-subj="popoverModal__deleteButton"]').should('be.disabled');

cy.get('input.euiFieldText[placeholder="delete"]').focus().type('delete', {
delay: 50,
});
cy.get('button[data-test-subj="popoverModal__deleteButton"]').should('not.be.disabled');
cy.get('button[data-test-subj="popoverModal__deleteButton"]').click();
cy.get('h2[data-test-subj="customPanels__noPanelsHome"]').should('exist');
});

it('Deletes panels', () => {
cy.get('input[data-test-subj="checkboxSelectAll"]').click();
cy.get('button[data-test-subj="operationalPanelsActionsButton"]').click();
openActionsDropdown();
cy.get('button[data-test-subj="deleteContextMenuItem"]').click();
cy.get('button[data-test-subj="popoverModal__deleteButton"]').should('be.disabled');

Expand All @@ -170,7 +201,7 @@ describe('Testing panels table', () => {

it('Create a panel for testing', () => {
// keep a panel for testing
cy.get('button[data-test-subj="customPanels__createNewPanels"]').click();
clickCreatePanelButton();
cy.get('input.euiFieldText').focus().type(TEST_PANEL, {
delay: 50,
});
Expand Down Expand Up @@ -470,8 +501,8 @@ describe('Clean up all test data', () => {
it('Deletes test panel', () => {
moveToPanelHome();
cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').trigger('mouseover').click();
cy.get('button[data-test-subj="operationalPanelsActionsButton"]').click();
cy.get('button[data-test-subj="deleteContextMenuItem"]').click();
openActionsDropdown();
clickDeleteAction();
cy.get('button.euiButton--danger').should('be.disabled');
cy.get('input.euiFieldText[placeholder="delete"]').focus().type('delete', {
delay: 50,
Expand All @@ -482,3 +513,169 @@ describe('Clean up all test data', () => {
cy.get('.euiTextAlign').contains('No Operational Panels').should('exist');
});
});


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

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

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

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 = () => {
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": TEST_PANEL,
"description": "",
"dateCreated": 1681127334085,
"dateModified": 1681127334085,
"timeRange": {
"to": "now",
"from": "now-1d"
},
"queryFilter": {
"query": "",
"language": "ppl"
},
"visualizations": [],
"applicationId": ""
}
}
}).then(response => console.log(response))

}

const createLegacyPanel = () => {
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: TEST_PANEL }
})
}

const expectUuid = (cell) => {
cell.find('a').its('href').should('match', uuidRx)
// const id = url.split('/').slice(-1)
// expect(id).not.to.match(uuidRx)
}

const expectLegacyId = (cell) => {
cell.find('a').its('href').should('not.match', uuidRx)
// const id = url.split('/').slice(-1)
// expect(id).not.to.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"]').eq(0).trigger('mouseover').click();
}

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

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

4 changes: 4 additions & 0 deletions .cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)


/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
require('cypress-watch-and-reload/plugins')(config)

return config
}
2 changes: 2 additions & 0 deletions .cypress/support/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
// https://on.cypress.io/configuration
// ***********************************************************

import 'cypress-watch-and-reload/support'

// Import commands.js using ES2015 syntax:
import './commands';

Expand Down
7 changes: 6 additions & 1 deletion 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 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_SAVED_OBJECT_TYPE = 'observability-panel';

export const CUSTOM_PANEL_SLICE = 'customPanel';
Loading

0 comments on commit 775a77e

Please sign in to comment.