feat: add confirmation for Datasource deletion operation (#11180)

* add confirmation for Datasource, refactor other components

* fix failing cypress tests

* fix Mongo_spec failing test
This commit is contained in:
Ayangade Adeoluwa 2022-02-18 07:58:36 +01:00 committed by GitHub
parent c03ecf2e13
commit ead4d875fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 71 additions and 47 deletions

View File

@ -120,6 +120,7 @@ describe("Entity explorer tests related to query and datasource", function() {
.click({ force: true });
cy.contains(".t--datasource-name", datasourceName).click();
cy.get(".t--delete-datasource").click();
cy.get("[data-cy=t--confirm-modal-btn]").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",

View File

@ -94,6 +94,7 @@ describe("Entity explorer tests related to copy query", function() {
cy.wait(2000);
cy.hoverAndClick();
cy.get(apiwidget.delete).click({ force: true });
cy.get("[data-cy=t--confirm-modal-btn]").click();
//This is check to make sure if a datasource is active 409
cy.wait("@deleteDatasource").should(
"have.nested.property",

View File

@ -196,6 +196,7 @@ describe("Create a query with a mongo datasource, run, save and then delete the
cy.NavigateToActiveTab();
cy.contains(".t--datasource-name", datasourceName).click();
cy.get(".t--delete-datasource").click();
cy.get("[data-cy=t--confirm-modal-btn]").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",
@ -288,6 +289,7 @@ describe("Create a query with a mongo datasource, run, save and then delete the
cy.NavigateToActiveTab();
cy.contains(".t--datasource-name", datasourceName).click();
cy.get(".t--delete-datasource").click();
cy.get("[data-cy=t--confirm-modal-btn]").click();
// cy.wait("@deleteDatasource").should(
// "have.nested.property",
// "response.body.responseMeta.status",

View File

@ -2186,6 +2186,7 @@ Cypress.Commands.add("testSaveDeleteDatasource", () => {
.click();
// delete datasource
cy.get(".t--delete-datasource").click();
cy.get("[data-cy=t--confirm-modal-btn]").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",
@ -2504,6 +2505,7 @@ Cypress.Commands.add("deleteDatasource", (datasourceName) => {
.click({ force: true });
cy.contains(".t--datasource-name", datasourceName).click();
cy.get(".t--delete-datasource").click();
cy.get("[data-cy=t--confirm-modal-btn]").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",
@ -2581,6 +2583,7 @@ Cypress.Commands.add("deleteJSObject", () => {
Cypress.Commands.add("deleteDataSource", () => {
cy.hoverAndClick();
cy.get(apiwidget.delete).click({ force: true });
cy.get("[data-cy=t--confirm-modal-btn]").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",

View File

@ -89,22 +89,22 @@ export const runAction = (id: string, paginationField?: PaginationField) => {
};
};
export const showRunActionConfirmModal = (show: boolean) => {
export const showActionConfirmationModal = (show: boolean) => {
return {
type: ReduxActionTypes.SHOW_RUN_ACTION_CONFIRM_MODAL,
type: ReduxActionTypes.SHOW_ACTION_MODAL,
payload: show,
};
};
export const cancelRunActionConfirmModal = () => {
export const cancelActionConfirmationModal = () => {
return {
type: ReduxActionTypes.CANCEL_RUN_ACTION_CONFIRM_MODAL,
type: ReduxActionTypes.CANCEL_ACTION_MODAL,
};
};
export const acceptRunActionConfirmModal = () => {
export const acceptActionConfirmationModal = () => {
return {
type: ReduxActionTypes.ACCEPT_RUN_ACTION_CONFIRM_MODAL,
type: ReduxActionTypes.CONFIRM_ACTION_MODAL,
};
};

View File

@ -260,9 +260,9 @@ export const ReduxActionTypes = {
DELETE_ACTION_INIT: "DELETE_ACTION_INIT",
SET_DATASOURCE_EDITOR_MODE: "SET_DATASOURCE_EDITOR_MODE",
DELETE_ACTION_SUCCESS: "DELETE_ACTION_SUCCESS",
SHOW_RUN_ACTION_CONFIRM_MODAL: "SHOW_RUN_ACTION_CONFIRM_MODAL",
CANCEL_RUN_ACTION_CONFIRM_MODAL: "CANCEL_RUN_ACTION_CONFIRM_MODAL",
ACCEPT_RUN_ACTION_CONFIRM_MODAL: "ACCEPT_RUN_ACTION_CONFIRM_MODAL",
SHOW_ACTION_MODAL: "SHOW_ACTION_MODAL",
CANCEL_ACTION_MODAL: "CANCEL_ACTION_MODAL",
CONFIRM_ACTION_MODAL: "CONFIRM_ACTION_MODAL",
CREATE_QUERY_INIT: "CREATE_QUERY_INIT",
ONBOARDING_CREATE_APPLICATION: "ONBOARDING_CREATE_APPLICATION",
FETCH_DATASOURCES_INIT: "FETCH_DATASOURCES_INIT",
@ -295,6 +295,7 @@ export const ReduxActionTypes = {
HISTORY_PUSH: "HISTORY_PUSH",
DELETE_DATASOURCE_INIT: "DELETE_DATASOURCE_INIT",
DELETE_DATASOURCE_SUCCESS: "DELETE_DATASOURCE_SUCCESS",
DELETE_DATASOURCE_CANCELLED: "DELETE_DATASOURCE_CANCELLED",
STORE_AS_DATASOURCE_INIT: "STORE_AS_DATASOURCE_INIT",
STORE_AS_DATASOURCE_UPDATE: "STORE_AS_DATASOURCE_UPDATE",
STORE_AS_DATASOURCE_COMPLETE: "STORE_AS_DATASOURCE_COMPLETE",

View File

@ -14,7 +14,7 @@ import {
getCurrentApplicationId,
getCurrentPageName,
} from "selectors/editorSelectors";
import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
import RequestConfirmationModal from "pages/Editor/RequestConfirmationModal";
import { getCurrentApplication } from "selectors/applicationSelectors";
import {
isPermitted,
@ -110,7 +110,7 @@ class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
pageId={this.props.match.params.pageId}
pageName={this.props.currentPageName}
/>
<ConfirmRunModal />
<RequestConfirmationModal />
</Section>
);
}

View File

@ -128,7 +128,8 @@ const SelectedDatasourceInfoContainer = styled.div`
align-items: center;
padding: 2px 8px;
background-color: ${Colors.LIGHT_GREEN_CYAN};
margin-right: 5px;
margin-right: 2px;
margin-left: 3px;
text-transform: uppercase;
& p {
font-style: normal;
@ -141,6 +142,7 @@ const SelectedDatasourceInfoContainer = styled.div`
letter-spacing: 0.4px;
text-transform: uppercase;
color: ${Colors.GREEN};
white-space: nowrap;
}
`;

View File

@ -3,9 +3,9 @@ import { connect } from "react-redux";
import { AppState } from "reducers";
import { Keys } from "@blueprintjs/core";
import {
showRunActionConfirmModal,
cancelRunActionConfirmModal,
acceptRunActionConfirmModal,
showActionConfirmationModal,
cancelActionConfirmationModal,
acceptActionConfirmationModal,
} from "actions/pluginActionActions";
import DialogComponent from "components/ads/DialogComponent";
import styled from "styled-components";
@ -33,7 +33,7 @@ const ModalFooter = styled.div`
}
`;
class ConfirmRunModal extends React.Component<Props> {
class RequestConfirmationModal extends React.Component<Props> {
addEventListener = () => {
document.addEventListener("keydown", this.onKeyUp);
};
@ -50,14 +50,14 @@ class ConfirmRunModal extends React.Component<Props> {
onConfirm = () => {
const { dispatch } = this.props;
dispatch(acceptRunActionConfirmModal());
dispatch(acceptActionConfirmationModal());
this.handleClose();
};
handleClose = () => {
const { dispatch } = this.props;
dispatch(showRunActionConfirmModal(false));
dispatch(cancelRunActionConfirmModal());
dispatch(showActionConfirmationModal(false));
dispatch(cancelActionConfirmationModal());
};
componentDidUpdate() {
@ -85,8 +85,9 @@ class ConfirmRunModal extends React.Component<Props> {
<ModalFooter>
<Button
category={Category.tertiary}
cypressSelector="t--cancel-modal-btn"
onClick={() => {
dispatch(cancelRunActionConfirmModal());
dispatch(cancelActionConfirmationModal());
this.handleClose();
}}
size={Size.medium}
@ -96,6 +97,7 @@ class ConfirmRunModal extends React.Component<Props> {
/>
<Button
category={Category.primary}
cypressSelector="t--confirm-modal-btn"
onClick={this.onConfirm}
size={Size.medium}
tag="button"
@ -112,4 +114,4 @@ const mapStateToProps = (state: AppState) => ({
isModalOpen: state.ui.confirmRunAction.modalOpen,
});
export default connect(mapStateToProps)(ConfirmRunModal);
export default connect(mapStateToProps)(RequestConfirmationModal);

View File

@ -20,7 +20,7 @@ import { editorInitializer } from "utils/EditorUtils";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import { getCurrentUser } from "selectors/usersSelectors";
import { User } from "constants/userConstants";
import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
import RequestConfirmationModal from "pages/Editor/RequestConfirmationModal";
import * as Sentry from "@sentry/react";
import { getTheme, ThemeMode } from "selectors/themeSelectors";
import { ThemeProvider } from "styled-components";
@ -228,7 +228,7 @@ class Editor extends Component<Props> {
<RepoLimitExceededErrorModal />
</GlobalHotKeys>
</div>
<ConfirmRunModal />
<RequestConfirmationModal />
</DndProvider>
</ThemeProvider>
);

View File

@ -197,6 +197,14 @@ const datasourceReducer = createReducer(initialState, {
),
};
},
[ReduxActionTypes.DELETE_DATASOURCE_CANCELLED]: (
state: DatasourceDataState,
) => {
return {
...state,
isDeleting: false,
};
},
[ReduxActionTypes.CREATE_DATASOURCE_SUCCESS]: (
state: DatasourceDataState,
action: ReduxAction<Datasource>,

View File

@ -6,7 +6,7 @@ const initialState: ConfirmRunActionReduxState = {
};
const confirmRunActionReducer = createReducer(initialState, {
[ReduxActionTypes.SHOW_RUN_ACTION_CONFIRM_MODAL]: (
[ReduxActionTypes.SHOW_ACTION_MODAL]: (
state: ConfirmRunActionReduxState,
action: ReduxAction<boolean>,
) => {

View File

@ -113,6 +113,7 @@ const explorerReducer = createReducer(initialState, {
[ReduxActionTypes.DELETE_DATASOURCE_INIT]: setUpdatingDatasourceEntity,
[ReduxActionErrorTypes.DELETE_DATASOURCE_ERROR]: setEntityUpdateError,
[ReduxActionTypes.DELETE_DATASOURCE_SUCCESS]: setEntityUpdateSuccess,
[ReduxActionTypes.DELETE_DATASOURCE_CANCELLED]: setEntityUpdateSuccess,
[ReduxActionTypes.UPDATE_DATASOURCE_INIT]: setUpdatingDatasourceEntity,
[ReduxActionErrorTypes.UPDATE_DATASOURCE_ERROR]: setEntityUpdateError,

View File

@ -1,18 +1,9 @@
import {
all,
call,
put,
race,
select,
take,
takeLatest,
} from "redux-saga/effects";
import { all, call, put, select, take, takeLatest } from "redux-saga/effects";
import {
executePluginActionError,
executePluginActionRequest,
executePluginActionSuccess,
runAction,
showRunActionConfirmModal,
updateAction,
} from "actions/pluginActionActions";
import {
@ -102,6 +93,7 @@ import {
executeAppAction,
TriggerMeta,
} from "sagas/ActionExecution/ActionExecutionSagas";
import { requestModalConfirmationSaga } from "sagas/UtilSagas";
enum ActionResponseDataTypes {
BINARY = "BINARY",
@ -237,17 +229,6 @@ function* evaluateActionParams(
return mapToPropList(actionParams);
}
function* confirmRunActionSaga() {
yield put(showRunActionConfirmModal(true));
const { accept } = yield race({
cancel: take(ReduxActionTypes.CANCEL_RUN_ACTION_CONFIRM_MODAL),
accept: take(ReduxActionTypes.ACCEPT_RUN_ACTION_CONFIRM_MODAL),
});
return !!accept;
}
export default function* executePluginActionTriggerSaga(
pluginAction: RunPluginActionDescription["payload"],
eventType: EventType,
@ -700,7 +681,7 @@ function* executePluginActionSaga(
}
if (pluginAction.confirmBeforeExecute) {
const confirmed = yield call(confirmRunActionSaga);
const confirmed = yield call(requestModalConfirmationSaga);
if (!confirmed) {
yield put({
type: ReduxActionTypes.RUN_ACTION_CANCELLED,

View File

@ -95,6 +95,7 @@ import { inGuidedTour } from "selectors/onboardingSelectors";
import { updateReplayEntity } from "actions/pageActions";
import OAuthApi from "api/OAuthApi";
import { AppState } from "reducers";
import { requestModalConfirmationSaga } from "sagas/UtilSagas";
function* fetchDatasourcesSaga() {
try {
@ -212,6 +213,15 @@ export function* deleteDatasourceSaga(
actionPayload: ReduxActionWithCallbacks<{ id: string }, unknown, unknown>,
) {
try {
// request confirmation from user before deleting datasource.
const confirmed = yield call(requestModalConfirmationSaga);
if (!confirmed) {
return yield put({
type: ReduxActionTypes.DELETE_DATASOURCE_CANCELLED,
});
}
const id = actionPayload.payload.id;
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.deleteDatasource(
id,

View File

@ -1,6 +1,7 @@
import { all, takeEvery } from "redux-saga/effects";
import { all, takeEvery, race, put, take } from "redux-saga/effects";
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
import history from "utils/history";
import { showActionConfirmationModal } from "actions/pluginActionActions";
function* redirectWindowLocationSaga(
actionPayload: ReduxAction<{ url: string }>,
@ -21,3 +22,14 @@ export default function* root() {
),
]);
}
export function* requestModalConfirmationSaga() {
yield put(showActionConfirmationModal(true));
const { accept } = yield race({
cancel: take(ReduxActionTypes.CANCEL_ACTION_MODAL),
accept: take(ReduxActionTypes.CONFIRM_ACTION_MODAL),
});
return !!accept;
}