PromucFlow_constructor/app/client/src/sagas/FormEvaluationSaga.ts
Ayush Pahwa 813d5850b0
fix: Update api params for dynamic fetched values (#11463)
* <feat> Updated payload for formEvals

- Added datasource and plugin ID to the payload

* <feat> Added IDs to dispatch calls

- Added datasource and plugin IDs to the dispatch calls so they can be used for dynamic query fetch

* <feat> Added type in evaluation saga

- Added datasource ID and plugin ID in the expected type for the eval function
- Updated call type from GET to POST for the dynamic trigger URLs

* <feat> Made params compulsory for editor config

- Params are now compulsory for any config defined in editor JSON for the dynamic value fetching

* <feat> Updated API definition

- Update the definition for the axios function from GET to POST

* <feat> Added configProperty to payload

- Added configProperty to the payload to allow for distinction between different components making a call to the server

* <chroe> Added null check

- Added null check to function call for datasource and plugin IDs in API since they are not compulsory in the type

* <refactor> Remove unused imports
2022-02-27 03:32:36 +00:00

197 lines
5.8 KiB
TypeScript

import { call, fork, take, select, put } from "redux-saga/effects";
import {
ReduxAction,
ReduxActionTypes,
} from "../constants/ReduxActionConstants";
import log from "loglevel";
import * as Sentry from "@sentry/react";
import { getFormEvaluationState } from "../selectors/formSelectors";
import { evalFormConfig } from "./EvaluationsSaga";
import {
ConditionalOutput,
DynamicValues,
FormEvalOutput,
FormEvaluationState,
} from "reducers/evaluationReducers/formEvaluationReducer";
import { FORM_EVALUATION_REDUX_ACTIONS } from "actions/evaluationActions";
import { ActionConfig } from "entities/Action";
import { FormConfig } from "components/formControls/BaseControl";
import PluginsApi from "api/PluginApi";
let isEvaluating = false; // Flag to maintain the queue of evals
export type FormEvalActionPayload = {
formId: string;
datasourceId?: string;
pluginId?: string;
actionConfiguration?: ActionConfig;
editorConfig?: FormConfig[];
settingConfig?: FormConfig[];
};
const evalQueue: ReduxAction<FormEvalActionPayload>[] = [];
// Function to set isEvaluating flag
const setIsEvaluating = (newState: boolean) => {
isEvaluating = newState;
};
// Function to extract all the objects that have to fetch dynamic values
const extractQueueOfValuesToBeFetched = (evalOutput: FormEvalOutput) => {
let output: Record<string, ConditionalOutput> = {};
Object.entries(evalOutput).forEach(([key, value]) => {
if (
"fetchDynamicValues" in value &&
!!value.fetchDynamicValues &&
"allowedToFetch" in value.fetchDynamicValues &&
value.fetchDynamicValues.allowedToFetch
) {
output = { ...output, [key]: value };
}
});
return output;
};
function* setFormEvaluationSagaAsync(
action: ReduxAction<FormEvalActionPayload>,
): any {
// We have to add a queue here because the eval cannot happen before the initial state is set
if (isEvaluating) {
evalQueue.push(action);
yield;
} else {
setIsEvaluating(true);
try {
// Get current state from redux
const currentEvalState: FormEvaluationState = yield select(
getFormEvaluationState,
);
// Trigger the worker to compute the new eval state
const workerResponse = yield call(evalFormConfig, {
...action,
currentEvalState,
});
// Update the eval state in redux only if it is not empty
if (!!workerResponse) {
yield put({
type: ReduxActionTypes.SET_FORM_EVALUATION,
payload: workerResponse,
});
}
setIsEvaluating(false);
// If there are any actions in the queue, run them
if (evalQueue.length > 0) {
const nextAction = evalQueue.shift() as ReduxAction<
FormEvalActionPayload
>;
yield fork(setFormEvaluationSagaAsync, nextAction);
} else {
// Once all the actions are done, extract the actions that need to be fetched dynamically
const formId = action.payload.formId;
const evalOutput = workerResponse[formId];
if (!!evalOutput && typeof evalOutput === "object") {
const queueOfValuesToBeFetched = extractQueueOfValuesToBeFetched(
evalOutput,
);
// Pass the queue to the saga to fetch the dynamic values
yield call(
fetchDynamicValuesSaga,
queueOfValuesToBeFetched,
formId,
evalOutput,
action.payload.datasourceId ? action.payload.datasourceId : "",
action.payload.pluginId ? action.payload.pluginId : "",
);
}
}
} catch (e) {
log.error(e);
setIsEvaluating(false);
}
}
}
// Function to fetch the dynamic values one by one from the queue
function* fetchDynamicValuesSaga(
queueOfValuesToBeFetched: Record<string, ConditionalOutput>,
formId: string,
evalOutput: FormEvalOutput,
datasourceId: string,
pluginId: string,
) {
for (const key of Object.keys(queueOfValuesToBeFetched)) {
evalOutput[key].fetchDynamicValues = yield call(
fetchDynamicValueSaga,
queueOfValuesToBeFetched[key],
Object.assign({}, evalOutput[key].fetchDynamicValues as DynamicValues),
formId,
datasourceId,
pluginId,
key,
);
}
// Set the values to the state once all values are fetched
yield put({
type: ReduxActionTypes.SET_FORM_EVALUATION,
payload: { [formId]: evalOutput },
});
}
function* fetchDynamicValueSaga(
value: ConditionalOutput,
dynamicFetchedValues: DynamicValues,
actionId: string,
datasourceId: string,
pluginId: string,
configProperty: string,
) {
try {
const { config } = value.fetchDynamicValues as DynamicValues;
const { params, url } = config;
dynamicFetchedValues.hasStarted = true;
// Call the API to fetch the dynamic values
const response = yield call(PluginsApi.fetchDynamicFormValues, url, {
actionId,
configProperty,
datasourceId,
pluginId,
...params,
});
dynamicFetchedValues.isLoading = false;
if (!!response && response instanceof Array) {
dynamicFetchedValues.data = response;
dynamicFetchedValues.hasFetchFailed = false;
} else {
dynamicFetchedValues.hasFetchFailed = true;
dynamicFetchedValues.data = [];
}
} catch (e) {
log.error(e);
dynamicFetchedValues.hasFetchFailed = true;
dynamicFetchedValues.isLoading = false;
dynamicFetchedValues.data = [];
}
return dynamicFetchedValues;
}
function* formEvaluationChangeListenerSaga() {
while (true) {
const action = yield take(FORM_EVALUATION_REDUX_ACTIONS);
yield fork(setFormEvaluationSagaAsync, action);
}
}
export default function* formEvaluationChangeListener() {
yield take(ReduxActionTypes.START_EVALUATION);
while (true) {
try {
yield call(formEvaluationChangeListenerSaga);
} catch (e) {
log.error(e);
Sentry.captureException(e);
}
}
}