PromucFlow_constructor/app/client/src/sagas/DatasourcesSagas.ts

578 lines
16 KiB
TypeScript
Raw Normal View History

import {
all,
put,
takeEvery,
select,
call,
take,
takeLatest,
} from "redux-saga/effects";
2020-05-19 06:10:59 +00:00
import { change, initialize, getFormValues } from "redux-form";
import _, { merge } from "lodash";
2019-11-07 09:32:38 +00:00
import {
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
2020-05-19 06:10:59 +00:00
ReduxFormActionTypes,
ReduxActionWithMeta,
ReduxActionWithCallbacks,
2019-11-25 05:07:27 +00:00
} from "constants/ReduxActionConstants";
import {
getCurrentApplicationId,
getCurrentPageId,
} from "selectors/editorSelectors";
2020-05-19 06:10:59 +00:00
import {
getPluginForm,
getDatasource,
getDatasourceDraft,
} from "selectors/entitiesSelector";
import {
selectPlugin,
changeDatasource,
setDatsourceEditorMode,
expandDatasourceEntity,
fetchDatasourceStructure,
createDatasourceFromForm,
} from "actions/datasourceActions";
2020-05-19 06:10:59 +00:00
import { fetchPluginForm } from "actions/pluginActions";
2019-11-25 05:07:27 +00:00
import { GenericApiResponse } from "api/ApiResponses";
import DatasourcesApi, { CreateDatasourceConfig } from "api/DatasourcesApi";
import { Datasource } from "entities/Datasource";
2020-04-29 09:23:23 +00:00
import PluginApi, { DatasourceForm } from "api/PluginApi";
import {
2020-11-19 11:30:22 +00:00
API_EDITOR_ID_URL,
DATA_SOURCES_EDITOR_ID_URL,
DATA_SOURCES_EDITOR_URL,
} from "constants/routes";
import history from "utils/history";
import { API_EDITOR_FORM_NAME, DATASOURCE_DB_FORM } from "constants/forms";
2019-11-13 07:34:59 +00:00
import { validateResponse } from "./ErrorSagas";
2020-03-06 04:59:24 +00:00
import AnalyticsUtil from "utils/AnalyticsUtil";
import { getFormData } from "selectors/formSelectors";
import { getCurrentOrgId } from "selectors/organizationSelectors";
import { AppState } from "reducers";
2020-11-24 07:01:37 +00:00
import { Variant } from "components/ads/common";
import { Toaster } from "components/ads/Toast";
import { getConfigInitialValues } from "components/formControls/utils";
2020-11-19 11:30:22 +00:00
import { setActionProperty } from "actions/actionActions";
2019-11-07 09:32:38 +00:00
function* fetchDatasourcesSaga() {
2019-11-13 07:34:59 +00:00
try {
const orgId = yield select(getCurrentOrgId);
const response: GenericApiResponse<Datasource[]> = yield DatasourcesApi.fetchDatasources(
orgId,
);
2019-11-13 07:34:59 +00:00
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS,
payload: response.data,
});
if (response.data.length) {
yield put(expandDatasourceEntity(response.data[0].id));
}
2019-11-13 07:34:59 +00:00
}
} catch (error) {
2019-11-07 09:32:38 +00:00
yield put({
type: ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR,
2019-11-13 07:34:59 +00:00
payload: { error },
2019-11-07 09:32:38 +00:00
});
}
}
export function* deleteDatasourceSaga(
actionPayload: ReduxAction<{ id: string }>,
) {
try {
const id = actionPayload.payload.id;
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.deleteDatasource(
id,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
if (
window.location.pathname ===
DATA_SOURCES_EDITOR_ID_URL(applicationId, pageId, id)
) {
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
}
2020-11-24 07:01:37 +00:00
Toaster.show({
text: `${response.data.name} datasource deleted`,
variant: Variant.success,
});
yield put({
type: ReduxActionTypes.DELETE_DATASOURCE_SUCCESS,
payload: response.data,
});
2020-05-19 06:10:59 +00:00
yield put({
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
payload: {
id: response.data.id,
},
});
}
} catch (error) {
2020-11-24 07:01:37 +00:00
Toaster.show({
text: error.message,
variant: Variant.danger,
});
yield put({
type: ReduxActionErrorTypes.DELETE_DATASOURCE_ERROR,
payload: { error, id: actionPayload.payload.id, show: false },
});
}
}
function* updateDatasourceSaga(
actionPayload: ReduxActionWithCallbacks<Datasource, unknown, unknown>,
) {
try {
const datasourcePayload = _.omit(actionPayload.payload, "name");
2020-08-26 05:24:44 +00:00
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.updateDatasource(
2020-08-26 05:24:44 +00:00
datasourcePayload,
datasourcePayload.id,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AnalyticsUtil.logEvent("SAVE_DATA_SOURCE", {
datasourceName: response.data.name,
});
2020-11-24 07:01:37 +00:00
Toaster.show({
text: `${response.data.name} Datasource updated`,
variant: Variant.success,
});
const state = yield select();
const expandDatasourceId = state.ui.datasourcePane.expandDatasourceId;
const datasourceStruture =
state.entities.datasources.structure[response.data.id];
yield put({
type: ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS,
payload: response.data,
});
if (actionPayload.onSuccess) {
yield put(actionPayload.onSuccess);
}
2020-05-19 06:10:59 +00:00
yield put({
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
payload: {
id: response.data.id,
},
});
yield put(
setDatsourceEditorMode({ id: datasourcePayload.id, viewMode: true }),
);
if (expandDatasourceId === response.data.id && !datasourceStruture) {
yield put(fetchDatasourceStructure(response.data.id));
}
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.UPDATE_DATASOURCE_ERROR,
payload: { error },
});
if (actionPayload.onError) {
yield put(actionPayload.onError);
}
}
}
function RedirectAuthorizationCodeSaga(
actionPayload: ReduxAction<{ datasourceId: string; pageId: string }>,
) {
window.location.href = `/api/v1/datasources/${actionPayload.payload.datasourceId}/pages/${actionPayload.payload.pageId}/code`;
}
2020-08-26 05:24:44 +00:00
function* saveDatasourceNameSaga(
actionPayload: ReduxAction<{ id: string; name: string }>,
) {
try {
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.updateDatasource(
{
name: actionPayload.payload.name,
},
actionPayload.payload.id,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.SAVE_DATASOURCE_NAME_SUCCESS,
payload: { ...response.data },
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.SAVE_DATASOURCE_NAME_ERROR,
payload: { id: actionPayload.payload.id },
});
}
}
function* handleDatasourceNameChangeFailureSaga(
action: ReduxAction<{ oldName: string }>,
) {
yield put(change(DATASOURCE_DB_FORM, "name", action.payload.oldName));
}
function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
const organizationId = yield select(getCurrentOrgId);
const { initialValues, values } = yield select(
getFormData,
DATASOURCE_DB_FORM,
);
const datasource = yield select(getDatasource, actionPayload.payload.id);
const payload = {
...actionPayload.payload,
name: datasource.name,
id: actionPayload.payload.id as any,
};
if (!_.isEqual(initialValues, values)) {
delete payload.id;
}
try {
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.testDatasource(
{
...payload,
organizationId,
},
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const responseData = response.data;
if (responseData.invalids && responseData.invalids.length) {
2020-11-24 07:01:37 +00:00
Toaster.show({
text: responseData.invalids[0],
variant: Variant.danger,
});
yield put({
type: ReduxActionErrorTypes.TEST_DATASOURCE_ERROR,
payload: { show: false },
});
} else {
AnalyticsUtil.logEvent("TEST_DATA_SOURCE_SUCCESS", {
datasource: payload.name,
});
2020-11-24 07:01:37 +00:00
Toaster.show({
text: `${payload.name} is valid`,
variant: Variant.success,
});
yield put({
type: ReduxActionTypes.TEST_DATASOURCE_SUCCESS,
payload: datasource,
});
}
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.TEST_DATASOURCE_ERROR,
payload: { error, show: false },
});
}
}
function* createDatasourceFromFormSaga(
actionPayload: ReduxAction<CreateDatasourceConfig>,
) {
try {
let formConfig;
const organizationId = yield select(getCurrentOrgId);
formConfig = yield select(getPluginForm, actionPayload.payload.pluginId);
if (!formConfig) {
const formConfigResponse: GenericApiResponse<DatasourceForm> = yield PluginApi.fetchFormConfig(
actionPayload.payload.pluginId,
);
yield validateResponse(formConfigResponse);
yield put({
type: ReduxActionTypes.FETCH_PLUGIN_FORM_SUCCESS,
payload: {
id: actionPayload.payload.pluginId,
...formConfigResponse.data,
},
});
formConfig = yield select(getPluginForm, actionPayload.payload.pluginId);
}
const initialValues = yield call(getConfigInitialValues, formConfig);
const payload = merge(initialValues, actionPayload.payload);
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
{
...payload,
organizationId,
},
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.UPDATE_DATASOURCE_REFS,
payload: response.data,
});
yield put({
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
payload: response.data,
});
yield put(
setDatsourceEditorMode({ id: response.data.id, viewMode: false }),
);
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
yield put(initialize(DATASOURCE_DB_FORM, _.omit(response.data, "name")));
history.push(
DATA_SOURCES_EDITOR_ID_URL(applicationId, pageId, response.data.id),
);
2020-11-24 07:01:37 +00:00
Toaster.show({
text: `${response.data.name} Datasource created`,
variant: Variant.success,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.CREATE_DATASOURCE_ERROR,
payload: { error },
});
}
}
2020-05-19 06:10:59 +00:00
function* updateDraftsSaga() {
const values = yield select(getFormValues(DATASOURCE_DB_FORM));
if (!values.id) return;
const datasource = yield select(getDatasource, values.id);
if (_.isEqual(values, datasource)) {
yield put({
type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT,
payload: { id: values.id },
});
} else {
yield put({
type: ReduxActionTypes.UPDATE_DATASOURCE_DRAFT,
payload: { id: values.id, draft: values },
});
}
}
function* changeDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
const { id, pluginId } = actionPayload.payload;
const datasource = actionPayload.payload;
const state = yield select();
const draft = yield select(getDatasourceDraft, id);
const formConfigs = state.entities.plugins.formConfigs;
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
let data;
if (_.isEmpty(draft)) {
data = actionPayload.payload;
} else {
data = draft;
}
yield put(initialize(DATASOURCE_DB_FORM, _.omit(data, ["name"])));
2020-05-19 06:10:59 +00:00
yield put(selectPlugin(pluginId));
if (!formConfigs[pluginId]) {
yield put(fetchPluginForm({ id: pluginId }));
}
history.push(
DATA_SOURCES_EDITOR_ID_URL(applicationId, pageId, datasource.id),
);
}
function* switchDatasourceSaga(action: ReduxAction<{ datasourceId: string }>) {
const { datasourceId } = action.payload;
const datasource = yield select((state: AppState) =>
state.entities.datasources.list.find(
(datasource: Datasource) => datasource.id === datasourceId,
),
);
2021-01-27 06:56:19 +00:00
if (datasource) {
yield put(changeDatasource(datasource));
}
}
2020-05-19 06:10:59 +00:00
function* formValueChangeSaga(
actionPayload: ReduxActionWithMeta<string, { field: string; form: string }>,
) {
2020-08-26 05:24:44 +00:00
const { form, field } = actionPayload.meta;
2020-05-19 06:10:59 +00:00
if (form !== DATASOURCE_DB_FORM) return;
2020-08-26 05:24:44 +00:00
if (field === "name") return;
2020-05-19 06:10:59 +00:00
yield all([call(updateDraftsSaga)]);
}
function* storeAsDatasourceSaga() {
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
let datasource = _.get(values, "datasource");
datasource = _.omit(datasource, ["name"]);
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
yield put(createDatasourceFromForm(datasource));
const createDatasourceSuccessAction = yield take(
ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
);
const createdDatasource = createDatasourceSuccessAction.payload;
// Update action to have this datasource
yield put(
setActionProperty({
actionId: values.id,
propertyName: "datasource",
value: createdDatasource,
}),
);
2020-11-19 11:30:22 +00:00
// Set datasource page to edit mode
yield put(
setDatsourceEditorMode({ id: createdDatasource.id, viewMode: false }),
);
yield put({
type: ReduxActionTypes.STORE_AS_DATASOURCE_UPDATE,
payload: {
pageId,
applicationId,
apiId: values.id,
datasourceId: createdDatasource.id,
},
});
yield put(changeDatasource(createdDatasource));
}
function* updateDatasourceSuccessSaga(action: ReduxAction<Datasource>) {
const state = yield select();
const actionRouteInfo = _.get(state, "ui.datasourcePane.actionRouteInfo");
const updatedDatasource = action.payload;
if (
actionRouteInfo &&
updatedDatasource.id === actionRouteInfo.datasourceId
) {
2020-11-19 11:30:22 +00:00
history.push(
API_EDITOR_ID_URL(
actionRouteInfo.applicationId,
actionRouteInfo.pageId,
actionRouteInfo.apiId,
),
);
}
yield put({
type: ReduxActionTypes.STORE_AS_DATASOURCE_COMPLETE,
});
}
function* fetchDatasourceStrucuture(action: ReduxAction<{ id: string }>) {
try {
const response: GenericApiResponse<any> = yield DatasourcesApi.fetchDatasourceStructure(
action.payload.id,
);
const isValidResponse = yield validateResponse(response, false);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.FETCH_DATASOURCE_STRUCTURE_SUCCESS,
payload: {
data: response.data,
datasourceId: action.payload.id,
},
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_DATASOURCE_STRUCTURE_ERROR,
payload: {
error,
show: false,
},
});
}
}
function* refreshDatasourceStrucuture(action: ReduxAction<{ id: string }>) {
try {
const response: GenericApiResponse<any> = yield DatasourcesApi.fetchDatasourceStructure(
action.payload.id,
true,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.REFRESH_DATASOURCE_STRUCTURE_SUCCESS,
payload: {
data: response.data,
datasourceId: action.payload.id,
},
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.REFRESH_DATASOURCE_STRUCTURE_ERROR,
payload: {
error,
show: false,
},
});
}
}
2019-11-07 09:32:38 +00:00
export function* watchDatasourcesSagas() {
yield all([
takeEvery(ReduxActionTypes.FETCH_DATASOURCES_INIT, fetchDatasourcesSaga),
takeEvery(
ReduxActionTypes.CREATE_DATASOURCE_FROM_FORM_INIT,
createDatasourceFromFormSaga,
),
takeEvery(ReduxActionTypes.UPDATE_DATASOURCE_INIT, updateDatasourceSaga),
2020-08-26 05:24:44 +00:00
takeEvery(ReduxActionTypes.SAVE_DATASOURCE_NAME, saveDatasourceNameSaga),
takeEvery(
ReduxActionErrorTypes.SAVE_DATASOURCE_NAME_ERROR,
handleDatasourceNameChangeFailureSaga,
),
takeEvery(ReduxActionTypes.TEST_DATASOURCE_INIT, testDatasourceSaga),
takeEvery(ReduxActionTypes.DELETE_DATASOURCE_INIT, deleteDatasourceSaga),
2020-05-19 06:10:59 +00:00
takeEvery(ReduxActionTypes.CHANGE_DATASOURCE, changeDatasourceSaga),
takeLatest(ReduxActionTypes.SWITCH_DATASOURCE, switchDatasourceSaga),
takeEvery(ReduxActionTypes.STORE_AS_DATASOURCE_INIT, storeAsDatasourceSaga),
takeEvery(
ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS,
updateDatasourceSuccessSaga,
),
takeEvery(
ReduxActionTypes.REDIRECT_AUTHORIZATION_CODE,
RedirectAuthorizationCodeSaga,
),
takeEvery(
ReduxActionTypes.FETCH_DATASOURCE_STRUCTURE_INIT,
fetchDatasourceStrucuture,
),
takeEvery(
ReduxActionTypes.REFRESH_DATASOURCE_STRUCTURE_INIT,
refreshDatasourceStrucuture,
),
2020-05-19 06:10:59 +00:00
// Intercepting the redux-form change actionType
takeEvery(ReduxFormActionTypes.VALUE_CHANGE, formValueChangeSaga),
2019-11-07 09:32:38 +00:00
]);
}