From 6cea0e80add343909ac23996fc29e47ab45721f0 Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Fri, 29 Nov 2019 05:22:49 +0000 Subject: [PATCH] Add support for plugin name based filtering --- app/client/package.json | 2 +- app/client/src/actions/pluginActions.ts | 8 ++++ app/client/src/api/PluginApi.ts | 18 +++++++++ .../form/fields/DatasourcesField.tsx | 10 +++-- .../src/constants/ApiEditorConstants.ts | 2 +- .../src/constants/ReduxActionConstants.tsx | 3 ++ .../src/pages/Editor/APIEditor/Form.tsx | 4 +- .../src/pages/Editor/APIEditor/index.tsx | 12 +++++- app/client/src/pages/Editor/ApiSidebar.tsx | 6 +++ .../entityReducers/datasourceReducer.ts | 4 +- .../src/reducers/entityReducers/index.tsx | 2 + .../reducers/entityReducers/pluginsReducer.ts | 37 +++++++++++++++++++ app/client/src/reducers/index.tsx | 2 + app/client/src/sagas/InitSagas.ts | 3 ++ app/client/src/sagas/PluginSagas.ts | 30 +++++++++++++++ app/client/src/sagas/index.tsx | 2 + app/client/src/selectors/entitiesSelector.ts | 11 ++++++ 17 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 app/client/src/actions/pluginActions.ts create mode 100644 app/client/src/api/PluginApi.ts create mode 100644 app/client/src/reducers/entityReducers/pluginsReducer.ts create mode 100644 app/client/src/sagas/PluginSagas.ts diff --git a/app/client/package.json b/app/client/package.json index 323e77bd63..04ed424355 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -83,7 +83,7 @@ "build": "craco build", "test": "craco test", "eject": "react-scripts eject", - "flow": "flow" + "start-prod": "REACT_APP_ENVIRONMENT=PRODUCTION craco start" }, "resolution": { "jest": "24.8.0" diff --git a/app/client/src/actions/pluginActions.ts b/app/client/src/actions/pluginActions.ts new file mode 100644 index 0000000000..8e38885bbb --- /dev/null +++ b/app/client/src/actions/pluginActions.ts @@ -0,0 +1,8 @@ +import { + ReduxActionTypes, + ReduxActionWithoutPayload, +} from "constants/ReduxActionConstants"; + +export const fetchPlugins = (): ReduxActionWithoutPayload => ({ + type: ReduxActionTypes.FETCH_PLUGINS_REQUEST, +}); diff --git a/app/client/src/api/PluginApi.ts b/app/client/src/api/PluginApi.ts new file mode 100644 index 0000000000..ebb5dbb751 --- /dev/null +++ b/app/client/src/api/PluginApi.ts @@ -0,0 +1,18 @@ +import Api from "./Api"; +import { AxiosPromise } from "axios"; +import { GenericApiResponse } from "api/ApiResponses"; + +export interface Plugin { + id: string; + name: string; + type: "API" | "DB"; +} + +class PluginsApi extends Api { + static url = "v1/plugins"; + static fetchPlugins(): AxiosPromise> { + return Api.get(PluginsApi.url); + } +} + +export default PluginsApi; diff --git a/app/client/src/components/editorComponents/form/fields/DatasourcesField.tsx b/app/client/src/components/editorComponents/form/fields/DatasourcesField.tsx index 432a1360c2..054143d214 100644 --- a/app/client/src/components/editorComponents/form/fields/DatasourcesField.tsx +++ b/app/client/src/components/editorComponents/form/fields/DatasourcesField.tsx @@ -6,7 +6,6 @@ import { AppState } from "reducers"; import { DatasourceDataState } from "reducers/entityReducers/datasourceReducer"; import _ from "lodash"; import { createDatasource } from "actions/datasourcesActions"; -import { REST_PLUGIN_ID } from "constants/ApiEditorConstants"; interface ReduxStateProps { datasources: DatasourceDataState; @@ -17,12 +16,14 @@ interface ReduxActionProps { interface ComponentProps { name: string; + pluginId: string; } const DatasourcesField = ( props: ReduxActionProps & ReduxStateProps & ComponentProps, ) => { const options = props.datasources.list + .filter(r => r.pluginId === props.pluginId) .filter(r => r.datasourceConfiguration !== null) .map(r => ({ label: r.datasourceConfiguration.url.endsWith("/") @@ -49,7 +50,10 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({ datasources: state.entities.datasources, }); -const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({ +const mapDispatchToProps = ( + dispatch: any, + ownProps: ComponentProps, +): ReduxActionProps => ({ createDatasource: (value: string) => dispatch( createDatasource({ @@ -59,7 +63,7 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({ // Datasource url should end with / url: value.endsWith("/") ? value : `${value}/`, }, - pluginId: REST_PLUGIN_ID, + pluginId: ownProps.pluginId, }), ), }); diff --git a/app/client/src/constants/ApiEditorConstants.ts b/app/client/src/constants/ApiEditorConstants.ts index 1dacfac0ff..d2fc8aca6b 100644 --- a/app/client/src/constants/ApiEditorConstants.ts +++ b/app/client/src/constants/ApiEditorConstants.ts @@ -31,4 +31,4 @@ export const FORM_INITIAL_VALUES = { }, }; -export const REST_PLUGIN_ID = "5ca385dc81b37f0004b4db85"; +export const PLUGIN_NAME = "RestTemplatePluginExecutor"; diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 37e80ccfa5..f41e726313 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -87,6 +87,8 @@ export const ReduxActionTypes: { [key: string]: string } = { UPDATE_API_DRAFT: "UPDATE_API_DRAFT", DELETE_API_DRAFT: "DELETE_API_DRAFT", UPDATE_ROUTES_PARAMS: "UPDATE_ROUTES_PARAMS", + FETCH_PLUGINS_REQUEST: "FETCH_PLUGINS_REQUEST", + FETCH_PLUGINS_SUCCESS: "FETCH_PLUGINS_SUCCESS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; @@ -121,6 +123,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = { FETCH_PAGE_LIST_ERROR: "FETCH_PAGE_LIST_ERROR", FETCH_APPLICATION_LIST_ERROR: "FETCH_APPLICATION_LIST_ERROR", CREATE_APPLICATION_ERROR: "CREATE_APPLICATION_ERROR", + FETCH_PLUGINS_ERROR: "FETCH_PLUGINS_ERROR", }; export const ReduxFormActionTypes: { [key: string]: string } = { diff --git a/app/client/src/pages/Editor/APIEditor/Form.tsx b/app/client/src/pages/Editor/APIEditor/Form.tsx index 37834b0a38..d6673ba7cb 100644 --- a/app/client/src/pages/Editor/APIEditor/Form.tsx +++ b/app/client/src/pages/Editor/APIEditor/Form.tsx @@ -69,6 +69,7 @@ const JSONEditorFieldWrapper = styled.div` `; interface APIFormProps { + pluginId: string; allowSave: boolean; allowPostBody: boolean; onSubmit: FormSubmitHandler; @@ -84,6 +85,7 @@ type Props = APIFormProps & InjectedFormProps; const ApiEditorForm: React.FC = (props: Props) => { const { + pluginId, allowSave, allowPostBody, onSaveClick, @@ -128,7 +130,7 @@ const ApiEditorForm: React.FC = (props: Props) => { name="actionConfiguration.httpMethod" options={HTTP_METHOD_OPTIONS} /> - + void; @@ -70,12 +72,19 @@ class ApiEditor extends React.Component { params: { apiId }, }, formData, + pluginId, } = this.props; const httpMethod = _.get(formData, "actionConfiguration.httpMethod"); + if (!pluginId) { + return ( + {"Plugin is not installed"} + ); + } return ( {apiId ? ( { } const mapStateToProps = (state: AppState): ReduxStateProps => ({ + pluginId: getPluginIdOfName(state, PLUGIN_NAME), actions: state.entities.actions, apiPane: state.ui.apiPane, formData: getFormValues(API_EDITOR_FORM_NAME)(state) as RestAction, diff --git a/app/client/src/pages/Editor/ApiSidebar.tsx b/app/client/src/pages/Editor/ApiSidebar.tsx index e34815ba8a..3b5b8cf49a 100644 --- a/app/client/src/pages/Editor/ApiSidebar.tsx +++ b/app/client/src/pages/Editor/ApiSidebar.tsx @@ -16,6 +16,8 @@ import { changeApi, initApiPane } from "actions/apiPaneActions"; import { RestAction } from "api/ActionAPI"; import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; import Fuse from "fuse.js"; +import { getPluginIdOfName } from "selectors/entitiesSelector"; +import { PLUGIN_NAME } from "constants/ApiEditorConstants"; const LoadingContainer = styled(CenteredWrapper)` height: 50%; @@ -133,6 +135,7 @@ const CreateApiWrapper = styled.div` interface ReduxStateProps { actions: ActionDataState; apiPane: ApiPaneReduxState; + pluginId: string | undefined; } interface ReduxDispatchProps { @@ -226,7 +229,9 @@ class ApiSidebar extends React.Component { apiPane: { isFetching, isSaving, drafts }, match, actions: { data }, + pluginId, } = this.props; + if (!pluginId) return null; const { isCreating, search, name } = this.state; const activeActionId = match.params.apiId; const fuse = new Fuse(data, fuseOptions); @@ -303,6 +308,7 @@ class ApiSidebar extends React.Component { } const mapStateToProps = (state: AppState): ReduxStateProps => ({ + pluginId: getPluginIdOfName(state, PLUGIN_NAME), actions: state.entities.actions, apiPane: state.ui.apiPane, }); diff --git a/app/client/src/reducers/entityReducers/datasourceReducer.ts b/app/client/src/reducers/entityReducers/datasourceReducer.ts index 84d22ee073..c68f10a627 100644 --- a/app/client/src/reducers/entityReducers/datasourceReducer.ts +++ b/app/client/src/reducers/entityReducers/datasourceReducer.ts @@ -1,7 +1,6 @@ import { createReducer } from "utils/AppsmithUtils"; import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants"; import { Datasource } from "api/DatasourcesApi"; -import { REST_PLUGIN_ID } from "constants/ApiEditorConstants"; export interface DatasourceDataState { list: Datasource[]; @@ -27,8 +26,7 @@ const datasourceReducer = createReducer(initialState, { return { ...state, loading: false, - // TODO(hetu) Once plugins are being pulled get Ids from there - list: action.payload.filter(r => r.pluginId === REST_PLUGIN_ID), + list: action.payload, }; }, [ReduxActionTypes.CREATE_DATASOURCE_SUCCESS]: ( diff --git a/app/client/src/reducers/entityReducers/index.tsx b/app/client/src/reducers/entityReducers/index.tsx index 9e1a3a74b7..84f2ca0d14 100644 --- a/app/client/src/reducers/entityReducers/index.tsx +++ b/app/client/src/reducers/entityReducers/index.tsx @@ -8,6 +8,7 @@ import propertyPaneConfigReducer from "./propertyPaneConfigReducer"; import datasourceReducer from "./datasourceReducer"; import bindingsReducer from "./bindingsReducer"; import pageListReducer from "./pageListReducer"; +import pluginsReducer from "reducers/entityReducers/pluginsReducer"; const entityReducer = combineReducers({ canvasWidgets: canvasWidgetsReducer, @@ -19,6 +20,7 @@ const entityReducer = combineReducers({ datasources: datasourceReducer, nameBindings: bindingsReducer, pageList: pageListReducer, + plugins: pluginsReducer, }); export default entityReducer; diff --git a/app/client/src/reducers/entityReducers/pluginsReducer.ts b/app/client/src/reducers/entityReducers/pluginsReducer.ts new file mode 100644 index 0000000000..70e2da40b4 --- /dev/null +++ b/app/client/src/reducers/entityReducers/pluginsReducer.ts @@ -0,0 +1,37 @@ +import { createReducer } from "utils/AppsmithUtils"; +import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants"; +import { Plugin } from "api/PluginApi"; + +export interface PluginDataState { + list: Plugin[]; + loading: boolean; +} + +const initialState: PluginDataState = { + list: [], + loading: false, +}; + +const pluginsReducer = createReducer(initialState, { + [ReduxActionTypes.FETCH_PLUGINS_REQUEST]: (state: PluginDataState) => { + return { ...state, loading: true }; + }, + [ReduxActionTypes.FETCH_PLUGINS_SUCCESS]: ( + state: PluginDataState, + action: ReduxAction, + ) => { + return { + ...state, + loading: false, + list: action.payload, + }; + }, + [ReduxActionTypes.FETCH_PLUGINS_ERROR]: (state: PluginDataState) => { + return { + ...state, + loading: false, + }; + }, +}); + +export default pluginsReducer; diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 9b4d83a76a..92a120194f 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -19,6 +19,7 @@ import { BindingsDataState } from "./entityReducers/bindingsReducer"; import { PageListReduxState } from "./entityReducers/pageListReducer"; import { ApiPaneReduxState } from "./uiReducers/apiPaneReducer"; import { RoutesParamsReducerState } from "reducers/uiReducers/routesParamsReducer"; +import { PluginDataState } from "reducers/entityReducers/pluginsReducer"; const appReducer = combineReducers({ entities: entityReducer, @@ -49,6 +50,7 @@ export interface AppState { datasources: DatasourceDataState; nameBindings: BindingsDataState; pageList: PageListReduxState; + plugins: PluginDataState; }; } diff --git a/app/client/src/sagas/InitSagas.ts b/app/client/src/sagas/InitSagas.ts index 6dfc38caaa..3446dbfdd6 100644 --- a/app/client/src/sagas/InitSagas.ts +++ b/app/client/src/sagas/InitSagas.ts @@ -10,6 +10,7 @@ import { fetchPageList } from "actions/pageActions"; import { fetchActions } from "actions/actionActions"; import { fetchDatasources } from "actions/datasourcesActions"; import { initBindingMapListener } from "actions/bindingActions"; +import { fetchPlugins } from "actions/pluginActions"; function* initializeEditorSaga( initializeEditorAction: ReduxAction, @@ -17,6 +18,7 @@ function* initializeEditorSaga( const { applicationId } = initializeEditorAction.payload; // Step 1: Start getting all the data needed by the yield all([ + put(fetchPlugins()), put(fetchPageList(applicationId)), put(fetchEditorConfigs()), put(initBindingMapListener()), @@ -25,6 +27,7 @@ function* initializeEditorSaga( ]); // Step 2: Wait for all data to be in the state yield all([ + take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS), take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS), take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS), take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS), diff --git a/app/client/src/sagas/PluginSagas.ts b/app/client/src/sagas/PluginSagas.ts new file mode 100644 index 0000000000..bc533e40ff --- /dev/null +++ b/app/client/src/sagas/PluginSagas.ts @@ -0,0 +1,30 @@ +import { all, takeEvery, call, put } from "redux-saga/effects"; +import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import PluginsApi from "api/PluginApi"; +import { validateResponse } from "sagas/ErrorSagas"; + +function* fetchPluginsSaga() { + try { + const pluginsResponse = yield call(PluginsApi.fetchPlugins); + const isValid = yield validateResponse(pluginsResponse); + if (isValid) { + yield put({ + type: ReduxActionTypes.FETCH_PLUGINS_SUCCESS, + payload: pluginsResponse.data, + }); + } + } catch (error) { + yield put({ + type: ReduxActionTypes.FETCH_PLUGINS_ERROR, + payload: { error }, + }); + } +} + +function* root() { + yield all([ + takeEvery(ReduxActionTypes.FETCH_PLUGINS_REQUEST, fetchPluginsSaga), + ]); +} + +export default root; diff --git a/app/client/src/sagas/index.tsx b/app/client/src/sagas/index.tsx index ce587ca1a4..97afa3ad22 100644 --- a/app/client/src/sagas/index.tsx +++ b/app/client/src/sagas/index.tsx @@ -13,6 +13,7 @@ import watchActionWidgetMapSagas, { watchPropertyAndBindingUpdate, } from "./ActionWidgetMapSagas"; import apiPaneSagas from "./ApiPaneSagas"; +import pluginSagas from "./PluginSagas"; export function* rootSaga() { yield all([ @@ -29,5 +30,6 @@ export function* rootSaga() { spawn(watchActionWidgetMapSagas), spawn(watchPropertyAndBindingUpdate), spawn(apiPaneSagas), + spawn(pluginSagas), ]); } diff --git a/app/client/src/selectors/entitiesSelector.ts b/app/client/src/selectors/entitiesSelector.ts index e9da541133..54bf9f2baa 100644 --- a/app/client/src/selectors/entitiesSelector.ts +++ b/app/client/src/selectors/entitiesSelector.ts @@ -1,3 +1,14 @@ import { AppState, DataTree } from "reducers"; export const getDataTree = (state: AppState): DataTree => state.entities; + +export const getPluginIdOfName = ( + state: AppState, + name: string, +): string | undefined => { + const plugin = state.entities.plugins.list.find( + plugin => plugin.name === name, + ); + if (!plugin) return undefined; + return plugin.id; +};