* refactor admin settings feature * separated save-restart bar to separate component * created new CE dir to facilitate code split * created separate ee dir and exporting everything we have in ce file. * little mod * minor fix * splitting settings types config * using object literals for category types instead of enums * CE: support use of component for each category * minor style fix * authentication page UI changes implemented * github signup doc url added back * removed comments * routing updates * made subcategories listing in left pane optional * added muted saml to auth listing * added breadcrumbs and enabled button * created separate component for auth page and auth config * added callout and disconnect components * updated breadcrumbs component * minor updates to common components * updated warning callout and added icon * ce: test cases fixed * updated test file name * warning banner callout added on auth page * updated callout banner for form login * CE: Split config files * CE: moved the window declaration in EE file as its dependency will be updated in EE * CE: Splitting ApiConstants and SocialLogin constants * CE: split login page * CE: moved getSocialLoginButtonProps func to EE file as it's dependencies will be updated in EE * added key icon * CE: created a factory class to share social auths list * Minor style fix for social btns * Updated the third party auth styles * Small fixes to styling * ce: splitting forms constants * breadcrumbs implemented for all pages in admin settings * Settings breadcrumbs separated * splitted settings breadcrumbs between ce and ee * renamed default import * minor style fix * added login form config. * updated login/signup pages to use form login disabled config * removed common functionality outside * implemented breadcrumb component from scratch without using blueprint * removed unwanted code * Small style update * updated breadcrumb categories file name and breadcrumb icon * added cypress tests for admin settings auth page * added comments * update locator for upgrade button * added link for intercom on upgrade button * removed unnecessary file * minor style fix * style fix for auth option cards * split messages constant * fixed imports for message constants splitting. * added message constants * updated unit test cases * fixed messages import in cypress index * fixed messages import again, cypress fails to read re-exported objs. * added OIDC auth method on authentication page * updated import statements from ee to @appsmith * removed dead code * updated read more link UI * PR comments fixes * some UI fixes * used color and fonts from theme * fixed some imports * fixed some imports * removed warning imports * updated OIDC logo and auth method desc copies * css changes * css changes * css changes * updated cypress test for breadcrumb * moved callout component to ads as calloutv2 * UI changes for form fields * updated css for spacing between form fields * added sub-text on auth pages * added active class for breadcrumb item * added config for disable signup toggle and fixed UI issues of restart banner * fixed admin settings page bugs * assigned true as default state for signup * fixed messages import statements * updated code for PR comments related suggestions * reverted file path change in cypress support * updated cypress test * updated cypress test Co-authored-by: Ankita Kinger <ankita@appsmith.com>
450 lines
14 KiB
TypeScript
450 lines
14 KiB
TypeScript
import {
|
|
all,
|
|
select,
|
|
put,
|
|
takeEvery,
|
|
debounce,
|
|
call,
|
|
take,
|
|
} from "redux-saga/effects";
|
|
import {
|
|
ReduxAction,
|
|
ReduxActionTypes,
|
|
ReduxActionErrorTypes,
|
|
} from "constants/ReduxActionConstants";
|
|
import {
|
|
getCurrentApplicationId,
|
|
getCurrentPageId,
|
|
} from "selectors/editorSelectors";
|
|
import { getJSCollection, getJSCollections } from "selectors/entitiesSelector";
|
|
import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
|
import { createNewJSFunctionName, getQueryParams } from "utils/AppsmithUtils";
|
|
import { JSCollection, JSAction } from "entities/JSCollection";
|
|
import { createJSCollectionRequest } from "actions/jsActionActions";
|
|
import { JS_COLLECTION_ID_URL } from "constants/routes";
|
|
import history from "utils/history";
|
|
import { executeFunction } from "./EvaluationsSaga";
|
|
import { getJSCollectionIdFromURL } from "pages/Editor/Explorer/helpers";
|
|
import {
|
|
getDifferenceInJSCollection,
|
|
JSUpdate,
|
|
pushLogsForObjectUpdate,
|
|
createDummyJSCollectionActions,
|
|
} from "../utils/JSPaneUtils";
|
|
import JSActionAPI, { RefactorAction } from "../api/JSActionAPI";
|
|
import {
|
|
updateJSCollectionSuccess,
|
|
refactorJSCollectionAction,
|
|
updateJSCollectionBodySuccess,
|
|
} from "actions/jsPaneActions";
|
|
import { getCurrentOrgId } from "selectors/organizationSelectors";
|
|
import { getPluginIdOfPackageName } from "sagas/selectors";
|
|
import { PluginType } from "entities/Action";
|
|
import { Toaster } from "components/ads/Toast";
|
|
import { Variant } from "components/ads/common";
|
|
import {
|
|
createMessage,
|
|
ERROR_JS_COLLECTION_RENAME_FAIL,
|
|
JS_EXECUTION_SUCCESS,
|
|
JS_EXECUTION_FAILURE,
|
|
JS_EXECUTION_FAILURE_TOASTER,
|
|
JS_FUNCTION_CREATE_SUCCESS,
|
|
JS_FUNCTION_DELETE_SUCCESS,
|
|
JS_FUNCTION_UPDATE_SUCCESS,
|
|
} from "@appsmith/constants/messages";
|
|
import { validateResponse } from "./ErrorSagas";
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
|
import { ENTITY_TYPE, PLATFORM_ERROR } from "entities/AppsmithConsole";
|
|
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
|
import PageApi from "api/PageApi";
|
|
import { updateCanvasWithDSL } from "sagas/PageSagas";
|
|
export const JS_PLUGIN_PACKAGE_NAME = "js-plugin";
|
|
import { updateReplayEntity } from "actions/pageActions";
|
|
|
|
function* handleCreateNewJsActionSaga(action: ReduxAction<{ pageId: string }>) {
|
|
const organizationId: string = yield select(getCurrentOrgId);
|
|
const applicationId = yield select(getCurrentApplicationId);
|
|
const { pageId } = action.payload;
|
|
const pluginId: string = yield select(
|
|
getPluginIdOfPackageName,
|
|
JS_PLUGIN_PACKAGE_NAME,
|
|
);
|
|
if (pageId && pluginId) {
|
|
const jsActions = yield select(getJSCollections);
|
|
const pageJSActions = jsActions.filter(
|
|
(a: JSCollectionData) => a.config.pageId === pageId,
|
|
);
|
|
const newJSCollectionName = createNewJSFunctionName(pageJSActions, pageId);
|
|
const { actions, body } = createDummyJSCollectionActions(
|
|
pageId,
|
|
organizationId,
|
|
);
|
|
yield put(
|
|
createJSCollectionRequest({
|
|
name: newJSCollectionName,
|
|
pageId,
|
|
organizationId,
|
|
pluginId,
|
|
body: body,
|
|
variables: [
|
|
{
|
|
name: "myVar1",
|
|
value: [],
|
|
},
|
|
{
|
|
name: "myVar2",
|
|
value: {},
|
|
},
|
|
],
|
|
actions: actions,
|
|
applicationId,
|
|
pluginType: PluginType.JS,
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
function* handleJSCollectionCreatedSaga(
|
|
actionPayload: ReduxAction<JSCollection>,
|
|
) {
|
|
const { id } = actionPayload.payload;
|
|
const applicationId = yield select(getCurrentApplicationId);
|
|
const pageId = yield select(getCurrentPageId);
|
|
history.push(JS_COLLECTION_ID_URL(applicationId, pageId, id, {}));
|
|
}
|
|
|
|
function* handleEachUpdateJSCollection(update: JSUpdate) {
|
|
const jsActionId = update.id;
|
|
const organizationId: string = yield select(getCurrentOrgId);
|
|
if (jsActionId) {
|
|
const jsAction: JSCollection = yield select(getJSCollection, jsActionId);
|
|
const parsedBody = update.parsedBody;
|
|
const jsActionTobeUpdated = JSON.parse(JSON.stringify(jsAction));
|
|
if (parsedBody) {
|
|
// jsActionTobeUpdated.body = jsAction.body;
|
|
const data = getDifferenceInJSCollection(parsedBody, jsAction);
|
|
if (data.nameChangedActions.length) {
|
|
for (let i = 0; i < data.nameChangedActions.length; i++) {
|
|
yield put(
|
|
refactorJSCollectionAction({
|
|
refactorAction: {
|
|
actionId: data.nameChangedActions[i].id,
|
|
collectionName: jsAction.name,
|
|
pageId: data.nameChangedActions[i].pageId,
|
|
oldName: data.nameChangedActions[i].oldName,
|
|
newName: data.nameChangedActions[i].newName,
|
|
},
|
|
actionCollection: jsActionTobeUpdated,
|
|
}),
|
|
);
|
|
}
|
|
} else {
|
|
let newActions: Partial<JSAction>[] = [];
|
|
let updateActions: JSAction[] = [];
|
|
let deletedActions: JSAction[] = [];
|
|
let updateCollection = false;
|
|
const changedVariables = data.changedVariables;
|
|
if (changedVariables.length) {
|
|
jsActionTobeUpdated.variables = parsedBody.variables;
|
|
updateCollection = true;
|
|
}
|
|
if (data.newActions.length) {
|
|
newActions = data.newActions;
|
|
for (let i = 0; i < data.newActions.length; i++) {
|
|
jsActionTobeUpdated.actions.push({
|
|
...data.newActions[i],
|
|
organizationId: organizationId,
|
|
});
|
|
}
|
|
updateCollection = true;
|
|
}
|
|
if (data.updateActions.length > 0) {
|
|
updateActions = data.updateActions;
|
|
let changedActions = [];
|
|
for (let i = 0; i < data.updateActions.length; i++) {
|
|
changedActions = jsActionTobeUpdated.actions.map(
|
|
(js: JSAction) =>
|
|
data.updateActions.find(
|
|
(update: JSAction) => update.id === js.id,
|
|
) || js,
|
|
);
|
|
}
|
|
updateCollection = true;
|
|
jsActionTobeUpdated.actions = changedActions;
|
|
}
|
|
if (data.deletedActions.length > 0) {
|
|
deletedActions = data.deletedActions;
|
|
const nonDeletedActions = jsActionTobeUpdated.actions.filter(
|
|
(js: JSAction) => {
|
|
return !data.deletedActions.find((deleted) => {
|
|
return deleted.id === js.id;
|
|
});
|
|
},
|
|
);
|
|
updateCollection = true;
|
|
jsActionTobeUpdated.actions = nonDeletedActions;
|
|
}
|
|
if (updateCollection) {
|
|
yield call(updateJSCollection, {
|
|
jsCollection: jsActionTobeUpdated,
|
|
newActions: newActions,
|
|
updatedActions: updateActions,
|
|
deletedActions: deletedActions,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function* makeUpdateJSCollection(jsUpdates: Record<string, JSUpdate>) {
|
|
yield all(
|
|
Object.keys(jsUpdates).map((key) =>
|
|
call(handleEachUpdateJSCollection, jsUpdates[key]),
|
|
),
|
|
);
|
|
}
|
|
|
|
function* updateJSCollection(data: {
|
|
jsCollection: JSCollection;
|
|
newActions?: Partial<JSAction>[];
|
|
updatedActions?: JSAction[];
|
|
deletedActions?: JSAction[];
|
|
}) {
|
|
let jsAction = {};
|
|
const jsActionId = getJSCollectionIdFromURL();
|
|
if (jsActionId) {
|
|
jsAction = yield select(getJSCollection, jsActionId);
|
|
}
|
|
try {
|
|
const { deletedActions, jsCollection, newActions, updatedActions } = data;
|
|
if (jsCollection) {
|
|
const response = yield JSActionAPI.updateJSCollection(jsCollection);
|
|
const isValidResponse = yield validateResponse(response);
|
|
if (isValidResponse) {
|
|
if (newActions && newActions.length) {
|
|
pushLogsForObjectUpdate(
|
|
newActions,
|
|
jsCollection,
|
|
createMessage(JS_FUNCTION_CREATE_SUCCESS),
|
|
);
|
|
}
|
|
if (updatedActions && updatedActions.length) {
|
|
pushLogsForObjectUpdate(
|
|
updatedActions,
|
|
jsCollection,
|
|
createMessage(JS_FUNCTION_UPDATE_SUCCESS),
|
|
);
|
|
}
|
|
if (deletedActions && deletedActions.length) {
|
|
pushLogsForObjectUpdate(
|
|
deletedActions,
|
|
jsCollection,
|
|
createMessage(JS_FUNCTION_DELETE_SUCCESS),
|
|
);
|
|
}
|
|
yield put(updateJSCollectionSuccess({ data: response?.data }));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.UPDATE_JS_ACTION_ERROR,
|
|
payload: { error, data: jsAction },
|
|
});
|
|
}
|
|
}
|
|
|
|
function* handleJSObjectNameChangeSuccessSaga(
|
|
action: ReduxAction<{ actionId: string }>,
|
|
) {
|
|
const { actionId } = action.payload;
|
|
const actionObj = yield select(getJSCollection, actionId);
|
|
yield take(ReduxActionTypes.FETCH_JS_ACTIONS_FOR_PAGE_SUCCESS);
|
|
if (!actionObj) {
|
|
// Error case, log to sentry
|
|
Toaster.show({
|
|
text: createMessage(ERROR_JS_COLLECTION_RENAME_FAIL, ""),
|
|
variant: Variant.danger,
|
|
});
|
|
|
|
return;
|
|
}
|
|
if (actionObj.pluginType === PluginType.API) {
|
|
const params = getQueryParams();
|
|
if (params.editName) {
|
|
params.editName = "false";
|
|
}
|
|
const applicationId = yield select(getCurrentApplicationId);
|
|
const pageId = yield select(getCurrentPageId);
|
|
history.push(JS_COLLECTION_ID_URL(applicationId, pageId, actionId, params));
|
|
}
|
|
}
|
|
|
|
function* handleExecuteJSFunctionSaga(
|
|
data: ReduxAction<{
|
|
collectionName: string;
|
|
action: JSAction;
|
|
collectionId: string;
|
|
}>,
|
|
): any {
|
|
const { action, collectionId, collectionName } = data.payload;
|
|
const actionId = action.id;
|
|
try {
|
|
const result = yield call(executeFunction, collectionName, action);
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.EXECUTE_JS_FUNCTION_SUCCESS,
|
|
payload: {
|
|
results: result,
|
|
collectionId,
|
|
actionId,
|
|
},
|
|
});
|
|
AppsmithConsole.info({
|
|
text: createMessage(JS_EXECUTION_SUCCESS),
|
|
source: {
|
|
type: ENTITY_TYPE.JSACTION,
|
|
name: collectionName + "." + action.name,
|
|
id: collectionId,
|
|
},
|
|
state: { response: result },
|
|
});
|
|
} catch (e) {
|
|
AppsmithConsole.addError({
|
|
id: actionId,
|
|
logType: LOG_TYPE.ACTION_EXECUTION_ERROR,
|
|
text: createMessage(JS_EXECUTION_FAILURE),
|
|
source: {
|
|
type: ENTITY_TYPE.JSACTION,
|
|
name: collectionName + "." + action.name,
|
|
id: collectionId,
|
|
},
|
|
messages: [
|
|
{
|
|
message: e.message,
|
|
type: PLATFORM_ERROR.PLUGIN_EXECUTION,
|
|
},
|
|
],
|
|
});
|
|
Toaster.show({
|
|
text: e.message || createMessage(JS_EXECUTION_FAILURE_TOASTER),
|
|
variant: Variant.danger,
|
|
showDebugButton: true,
|
|
});
|
|
}
|
|
}
|
|
|
|
function* handleUpdateJSCollectionBody(
|
|
actionPayload: ReduxAction<{ body: string; id: string; isReplay: boolean }>,
|
|
) {
|
|
const jsCollection = yield select(getJSCollection, actionPayload.payload.id);
|
|
jsCollection.body = actionPayload.payload.body;
|
|
try {
|
|
if (jsCollection) {
|
|
const response = yield JSActionAPI.updateJSCollection(jsCollection);
|
|
const isValidResponse = yield validateResponse(response);
|
|
if (isValidResponse) {
|
|
yield put(updateJSCollectionBodySuccess({ data: response?.data }));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.UPDATE_JS_ACTION_BODY_ERROR,
|
|
payload: { error, data: jsCollection },
|
|
});
|
|
}
|
|
if (!actionPayload.payload.isReplay)
|
|
yield put(
|
|
updateReplayEntity(
|
|
actionPayload.payload.id,
|
|
{
|
|
id: actionPayload.payload.id,
|
|
body: actionPayload.payload.body,
|
|
},
|
|
ENTITY_TYPE.JSACTION,
|
|
),
|
|
);
|
|
}
|
|
|
|
function* handleRefactorJSActionNameSaga(
|
|
data: ReduxAction<{
|
|
refactorAction: RefactorAction;
|
|
actionCollection: JSCollection;
|
|
}>,
|
|
) {
|
|
const pageResponse = yield call(PageApi.fetchPage, {
|
|
id: data.payload.refactorAction.pageId,
|
|
});
|
|
const isPageRequestSuccessful = yield validateResponse(pageResponse);
|
|
if (isPageRequestSuccessful) {
|
|
// get the layoutId from the page response
|
|
const layoutId = pageResponse.data.layouts[0].id;
|
|
const requestData = {
|
|
refactorAction: {
|
|
...data.payload.refactorAction,
|
|
layoutId: layoutId,
|
|
},
|
|
actionCollection: data.payload.actionCollection,
|
|
};
|
|
// call to refactor action
|
|
try {
|
|
const refactorResponse = yield JSActionAPI.updateJSCollectionActionRefactor(
|
|
requestData,
|
|
);
|
|
|
|
const isRefactorSuccessful = yield validateResponse(refactorResponse);
|
|
|
|
const currentPageId = yield select(getCurrentPageId);
|
|
|
|
if (isRefactorSuccessful) {
|
|
yield put({
|
|
type: ReduxActionTypes.REFACTOR_JS_ACTION_NAME_SUCCESS,
|
|
payload: { collectionId: data.payload.actionCollection.id },
|
|
});
|
|
if (currentPageId === data.payload.refactorAction.pageId) {
|
|
yield updateCanvasWithDSL(
|
|
refactorResponse.data,
|
|
data.payload.refactorAction.pageId,
|
|
layoutId,
|
|
);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.REFACTOR_JS_ACTION_NAME_ERROR,
|
|
payload: { collectionId: data.payload.actionCollection.id },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function* root() {
|
|
yield all([
|
|
takeEvery(
|
|
ReduxActionTypes.CREATE_NEW_JS_ACTION,
|
|
handleCreateNewJsActionSaga,
|
|
),
|
|
takeEvery(
|
|
ReduxActionTypes.CREATE_JS_ACTION_SUCCESS,
|
|
handleJSCollectionCreatedSaga,
|
|
),
|
|
takeEvery(
|
|
ReduxActionTypes.SAVE_JS_COLLECTION_NAME_SUCCESS,
|
|
handleJSObjectNameChangeSuccessSaga,
|
|
),
|
|
takeEvery(
|
|
ReduxActionTypes.EXECUTE_JS_FUNCTION_INIT,
|
|
handleExecuteJSFunctionSaga,
|
|
),
|
|
takeEvery(
|
|
ReduxActionTypes.REFACTOR_JS_ACTION_NAME,
|
|
handleRefactorJSActionNameSaga,
|
|
),
|
|
debounce(
|
|
100,
|
|
ReduxActionTypes.UPDATE_JS_ACTION_BODY_INIT,
|
|
handleUpdateJSCollectionBody,
|
|
),
|
|
]);
|
|
}
|