Add support for plugin name based filtering
This commit is contained in:
parent
743984938d
commit
6cea0e80ad
|
|
@ -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"
|
||||
|
|
|
|||
8
app/client/src/actions/pluginActions.ts
Normal file
8
app/client/src/actions/pluginActions.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import {
|
||||
ReduxActionTypes,
|
||||
ReduxActionWithoutPayload,
|
||||
} from "constants/ReduxActionConstants";
|
||||
|
||||
export const fetchPlugins = (): ReduxActionWithoutPayload => ({
|
||||
type: ReduxActionTypes.FETCH_PLUGINS_REQUEST,
|
||||
});
|
||||
18
app/client/src/api/PluginApi.ts
Normal file
18
app/client/src/api/PluginApi.ts
Normal file
|
|
@ -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<GenericApiResponse<Plugin[]>> {
|
||||
return Api.get(PluginsApi.url);
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginsApi;
|
||||
|
|
@ -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,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,4 +31,4 @@ export const FORM_INITIAL_VALUES = {
|
|||
},
|
||||
};
|
||||
|
||||
export const REST_PLUGIN_ID = "5ca385dc81b37f0004b4db85";
|
||||
export const PLUGIN_NAME = "RestTemplatePluginExecutor";
|
||||
|
|
|
|||
|
|
@ -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 } = {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ const JSONEditorFieldWrapper = styled.div`
|
|||
`;
|
||||
|
||||
interface APIFormProps {
|
||||
pluginId: string;
|
||||
allowSave: boolean;
|
||||
allowPostBody: boolean;
|
||||
onSubmit: FormSubmitHandler<RestAction>;
|
||||
|
|
@ -84,6 +85,7 @@ type Props = APIFormProps & InjectedFormProps<RestAction, APIFormProps>;
|
|||
|
||||
const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||
const {
|
||||
pluginId,
|
||||
allowSave,
|
||||
allowPostBody,
|
||||
onSaveClick,
|
||||
|
|
@ -128,7 +130,7 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
|||
name="actionConfiguration.httpMethod"
|
||||
options={HTTP_METHOD_OPTIONS}
|
||||
/>
|
||||
<DatasourcesField name="datasource.id" />
|
||||
<DatasourcesField name="datasource.id" pluginId={pluginId} />
|
||||
<TextField
|
||||
placeholder="API Path"
|
||||
name="actionConfiguration.path"
|
||||
|
|
|
|||
|
|
@ -15,13 +15,15 @@ import { API_EDITOR_FORM_NAME } from "constants/forms";
|
|||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
||||
import styled from "styled-components";
|
||||
import { HTTP_METHODS } from "constants/ApiEditorConstants";
|
||||
import { HTTP_METHODS, PLUGIN_NAME } from "constants/ApiEditorConstants";
|
||||
import _ from "lodash";
|
||||
import { getPluginIdOfName } from "selectors/entitiesSelector";
|
||||
|
||||
interface ReduxStateProps {
|
||||
actions: ActionDataState;
|
||||
apiPane: ApiPaneReduxState;
|
||||
formData: RestAction;
|
||||
pluginId: string | undefined;
|
||||
}
|
||||
interface ReduxActionProps {
|
||||
submitForm: (name: string) => void;
|
||||
|
|
@ -70,12 +72,19 @@ class ApiEditor extends React.Component<Props> {
|
|||
params: { apiId },
|
||||
},
|
||||
formData,
|
||||
pluginId,
|
||||
} = this.props;
|
||||
const httpMethod = _.get(formData, "actionConfiguration.httpMethod");
|
||||
if (!pluginId) {
|
||||
return (
|
||||
<EmptyStateContainer>{"Plugin is not installed"}</EmptyStateContainer>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
{apiId ? (
|
||||
<ApiEditorForm
|
||||
pluginId={pluginId}
|
||||
allowSave={apiId in drafts}
|
||||
allowPostBody={httpMethod && httpMethod !== HTTP_METHODS[0]}
|
||||
isSaving={isSaving}
|
||||
|
|
@ -97,6 +106,7 @@ class ApiEditor extends React.Component<Props> {
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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<Props, State> {
|
|||
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<Props, State> {
|
|||
}
|
||||
|
||||
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
||||
pluginId: getPluginIdOfName(state, PLUGIN_NAME),
|
||||
actions: state.entities.actions,
|
||||
apiPane: state.ui.apiPane,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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]: (
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
37
app/client/src/reducers/entityReducers/pluginsReducer.ts
Normal file
37
app/client/src/reducers/entityReducers/pluginsReducer.ts
Normal file
|
|
@ -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<Plugin[]>,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
list: action.payload,
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.FETCH_PLUGINS_ERROR]: (state: PluginDataState) => {
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default pluginsReducer;
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<InitializeEditorPayload>,
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
30
app/client/src/sagas/PluginSagas.ts
Normal file
30
app/client/src/sagas/PluginSagas.ts
Normal file
|
|
@ -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;
|
||||
|
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user