This commit is contained in:
parent
a94c75b868
commit
c8a132f88d
|
|
@ -84,3 +84,7 @@ export const fetchPluginFormConfig = ({
|
|||
export const fetchDefaultPlugins = (): ReduxActionWithoutPayload => ({
|
||||
type: ReduxActionTypes.GET_DEFAULT_PLUGINS_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchUpcomingPlugins = (): ReduxActionWithoutPayload => ({
|
||||
type: ReduxActionTypes.GET_UPCOMING_PLUGINS_REQUEST,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ import type { AxiosPromise } from "axios";
|
|||
import type { ApiResponse } from "api/ApiResponses";
|
||||
import type { DependencyMap } from "utils/DynamicBindingUtils";
|
||||
import { FILE_UPLOAD_TRIGGER_TIMEOUT_MS } from "ee/constants/ApiConstants";
|
||||
import type { DefaultPlugin, Plugin } from "entities/Plugin";
|
||||
import type {
|
||||
DefaultPlugin,
|
||||
Plugin,
|
||||
UpcomingIntegration,
|
||||
} from "entities/Plugin";
|
||||
import { objectKeys } from "@appsmith/utils";
|
||||
|
||||
export interface PluginFormPayload {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -55,6 +60,12 @@ class PluginsApi extends Api {
|
|||
return Api.get(PluginsApi.url + `/default/icons`);
|
||||
}
|
||||
|
||||
static async fetchUpcomingIntegrations(): Promise<
|
||||
AxiosPromise<ApiResponse<UpcomingIntegration[]>>
|
||||
> {
|
||||
return Api.get(PluginsApi.url + "/upcoming-integrations");
|
||||
}
|
||||
|
||||
static async uploadFiles(
|
||||
pluginId: string,
|
||||
files: File[],
|
||||
|
|
@ -70,7 +81,7 @@ class PluginsApi extends Api {
|
|||
});
|
||||
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key) => {
|
||||
objectKeys(params).forEach((key) => {
|
||||
formData.append(key, params[key]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
73
app/client/src/api/__tests__/PluginApi.test.ts
Normal file
73
app/client/src/api/__tests__/PluginApi.test.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import PluginsApi from "api/PluginApi";
|
||||
import Api from "api/Api";
|
||||
import type { UpcomingIntegration } from "entities/Plugin";
|
||||
|
||||
// Mock the Api module with a class that can be extended
|
||||
jest.mock("api/Api", () => {
|
||||
return {
|
||||
// Export a class that can be extended
|
||||
__esModule: true,
|
||||
default: class MockApi {
|
||||
static get: jest.Mock = jest.fn();
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("PluginsApi", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("fetchUpcomingIntegrations", () => {
|
||||
it("should call the correct API endpoint", async () => {
|
||||
// Setup mock API response
|
||||
const mockResponse = {
|
||||
data: {
|
||||
responseMeta: {
|
||||
success: true,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
name: "Test Integration",
|
||||
iconLocation: "test-icon-location",
|
||||
},
|
||||
{
|
||||
name: "Another Test",
|
||||
iconLocation: "another-test-icon",
|
||||
},
|
||||
] as UpcomingIntegration[],
|
||||
},
|
||||
};
|
||||
|
||||
(Api.get as jest.Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
// Call the function
|
||||
const result = await PluginsApi.fetchUpcomingIntegrations();
|
||||
|
||||
// Verify API was called correctly
|
||||
expect(Api.get).toHaveBeenCalledWith(
|
||||
PluginsApi.url + "/upcoming-integrations",
|
||||
);
|
||||
|
||||
// Verify response matches mock
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it("should handle API errors", async () => {
|
||||
// Setup mock API to throw error
|
||||
const mockError = new Error("API error");
|
||||
|
||||
(Api.get as jest.Mock).mockRejectedValue(mockError);
|
||||
|
||||
// Call the function and expect it to throw
|
||||
await expect(PluginsApi.fetchUpcomingIntegrations()).rejects.toThrow(
|
||||
mockError,
|
||||
);
|
||||
|
||||
// Verify API was called
|
||||
expect(Api.get).toHaveBeenCalledWith(
|
||||
PluginsApi.url + "/upcoming-integrations",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -751,12 +751,15 @@ const PluginActionTypes = {
|
|||
GET_PLUGIN_FORM_CONFIG_INIT: "GET_PLUGIN_FORM_CONFIG_INIT",
|
||||
GET_DEFAULT_PLUGINS_REQUEST: "GET_DEFAULT_PLUGINS_REQUEST",
|
||||
GET_DEFAULT_PLUGINS_SUCCESS: "GET_DEFAULT_PLUGINS_SUCCESS",
|
||||
GET_UPCOMING_PLUGINS_REQUEST: "GET_UPCOMING_PLUGINS_REQUEST",
|
||||
GET_UPCOMING_PLUGINS_SUCCESS: "GET_UPCOMING_PLUGINS_SUCCESS",
|
||||
};
|
||||
const PluginActionErrorTypes = {
|
||||
FETCH_PLUGINS_ERROR: "FETCH_PLUGINS_ERROR",
|
||||
FETCH_PLUGIN_FORM_CONFIGS_ERROR: "FETCH_PLUGIN_FORM_CONFIGS_ERROR",
|
||||
FETCH_PLUGIN_FORM_ERROR: "FETCH_PLUGIN_FORM_ERROR",
|
||||
GET_DEFAULT_PLUGINS_ERROR: "GET_DEFAULT_PLUGINS_ERROR",
|
||||
GET_UPCOMING_PLUGINS_ERROR: "GET_UPCOMING_PLUGINS_ERROR",
|
||||
};
|
||||
|
||||
const UQIFormActionTypes = {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type { ExplorerURLParams } from "ee/pages/Editor/Explorer/helpers";
|
|||
import type { DependentFeatureFlags } from "ee/selectors/engineSelectors";
|
||||
import { fetchDatasources } from "actions/datasourceActions";
|
||||
import { fetchPageDSLs } from "actions/pageActions";
|
||||
import { fetchPlugins } from "actions/pluginActions";
|
||||
import { fetchPlugins, fetchUpcomingPlugins } from "actions/pluginActions";
|
||||
import type { Plugin } from "entities/Plugin";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useParams } from "react-router";
|
||||
|
|
@ -37,6 +37,7 @@ export const getPageDependencyActions = (
|
|||
fetchPlugins({ plugins }),
|
||||
fetchDatasources({ datasources }),
|
||||
fetchPageDSLs({ pagesWithMigratedDsl }),
|
||||
fetchUpcomingPlugins(), // Not adding success and error actions for this as it's not a blocker for the app to load
|
||||
] as Array<ReduxAction<unknown>>;
|
||||
|
||||
const successActions = [
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ const defaultStoreState = {
|
|||
...unitTestBaseMockStore.entities,
|
||||
plugins: {
|
||||
list: [],
|
||||
upcomingPlugins: {
|
||||
list: [],
|
||||
loading: false,
|
||||
},
|
||||
},
|
||||
datasources: {
|
||||
list: [],
|
||||
|
|
|
|||
|
|
@ -1799,3 +1799,8 @@ export const getJSCollectionActionSchemaDirtyState = createSelector(
|
|||
return action.isDirtyMap?.SCHEMA_GENERATION;
|
||||
},
|
||||
);
|
||||
|
||||
export const getUpcomingPlugins = createSelector(
|
||||
(state: AppState) => state.entities.plugins.upcomingPlugins,
|
||||
(upcomingPlugins) => upcomingPlugins.list,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -91,3 +91,8 @@ export interface DefaultPlugin {
|
|||
iconLocation?: string;
|
||||
allowUserDatasources?: boolean;
|
||||
}
|
||||
|
||||
export interface UpcomingIntegration {
|
||||
name: string;
|
||||
iconLocation: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ import {
|
|||
type Plugin,
|
||||
PluginPackageName,
|
||||
PluginType,
|
||||
type UpcomingIntegration,
|
||||
} from "entities/Plugin";
|
||||
import { getQueryParams } from "utils/URLUtils";
|
||||
import {
|
||||
getGenerateCRUDEnabledPluginMap,
|
||||
getPlugins,
|
||||
getUpcomingPlugins,
|
||||
} from "ee/selectors/entitiesSelector";
|
||||
import { getIsGeneratePageInitiator } from "utils/GenerateCrudUtil";
|
||||
import { getAssetUrl, isAirgapped } from "ee/utils/airgapHelpers";
|
||||
|
|
@ -43,10 +45,7 @@ import {
|
|||
import scrollIntoView from "scroll-into-view-if-needed";
|
||||
import PremiumDatasources from "./PremiumDatasources";
|
||||
import { pluginSearchSelector } from "./CreateNewDatasourceHeader";
|
||||
import {
|
||||
getFilteredPremiumIntegrations,
|
||||
type PremiumIntegration,
|
||||
} from "./PremiumDatasources/Constants";
|
||||
import { getFilteredUpcomingIntegrations } from "./PremiumDatasources/Constants";
|
||||
import { getDatasourcesLoadingState } from "selectors/datasourceSelectors";
|
||||
import { getIDETypeByUrl } from "ee/entities/IDE/utils";
|
||||
import type { IDEType } from "ee/IDE/Interfaces/IDETypes";
|
||||
|
|
@ -75,7 +74,7 @@ interface CreateAPIOrSaasPluginsProps {
|
|||
apiType: string,
|
||||
) => void;
|
||||
isPremiumDatasourcesViewEnabled?: boolean;
|
||||
premiumPlugins: PremiumIntegration[];
|
||||
upcomingIntegrations: UpcomingIntegration[];
|
||||
authApiPlugin?: Plugin;
|
||||
restAPIVisible?: boolean;
|
||||
graphQLAPIVisible?: boolean;
|
||||
|
|
@ -235,7 +234,7 @@ function APIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) {
|
|||
/>
|
||||
))}
|
||||
{!props.isIntegrationsEnabledForPaid && (
|
||||
<PremiumDatasources plugins={props.premiumPlugins} />
|
||||
<PremiumDatasources plugins={props.upcomingIntegrations} />
|
||||
)}
|
||||
</DatasourceContainer>
|
||||
);
|
||||
|
|
@ -263,7 +262,7 @@ function CreateAPIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) {
|
|||
if (isAirgappedInstance && props.showSaasAPIs) return null;
|
||||
|
||||
if (
|
||||
props.premiumPlugins.length === 0 &&
|
||||
props.upcomingIntegrations.length === 0 &&
|
||||
props.plugins.length === 0 &&
|
||||
!props.restAPIVisible &&
|
||||
!props.graphQLAPIVisible
|
||||
|
|
@ -281,7 +280,8 @@ function CreateAPIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) {
|
|||
</DatasourceSectionHeading>
|
||||
<APIOrSaasPlugins {...props} />
|
||||
</DatasourceSection>
|
||||
{props.premiumPlugins.length > 0 && props.isIntegrationsEnabledForPaid ? (
|
||||
{props.upcomingIntegrations.length > 0 &&
|
||||
props.isIntegrationsEnabledForPaid ? (
|
||||
<DatasourceSection id="upcoming-saas-integrations">
|
||||
<DatasourceSectionHeading kind="heading-m">
|
||||
{createMessage(UPCOMING_SAAS_INTEGRATIONS)}
|
||||
|
|
@ -289,7 +289,7 @@ function CreateAPIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) {
|
|||
<DatasourceContainer data-testid="upcoming-datasource-card-container">
|
||||
<PremiumDatasources
|
||||
isIntegrationsEnabledForPaid
|
||||
plugins={props.premiumPlugins}
|
||||
plugins={props.upcomingIntegrations}
|
||||
/>
|
||||
</DatasourceContainer>
|
||||
</DatasourceSection>
|
||||
|
|
@ -310,6 +310,8 @@ const mapStateToProps = (
|
|||
pluginSearchSelector(state, "search") || ""
|
||||
).toLocaleLowerCase();
|
||||
|
||||
const upcomingPlugins = getUpcomingPlugins(state);
|
||||
|
||||
const allPlugins = getPlugins(state);
|
||||
|
||||
let plugins = allPlugins.filter((p) =>
|
||||
|
|
@ -354,15 +356,16 @@ const mapStateToProps = (
|
|||
plugin.name.toLocaleLowerCase(),
|
||||
);
|
||||
|
||||
const premiumPlugins =
|
||||
const upcomingIntegrations =
|
||||
props.showSaasAPIs && props.isPremiumDatasourcesViewEnabled
|
||||
? (filterSearch(
|
||||
getFilteredPremiumIntegrations(
|
||||
getFilteredUpcomingIntegrations(
|
||||
isExternalSaasEnabled || isIntegrationsEnabledForPaid,
|
||||
pluginNames,
|
||||
upcomingPlugins,
|
||||
),
|
||||
searchedPlugin,
|
||||
) as PremiumIntegration[])
|
||||
) as UpcomingIntegration[])
|
||||
: [];
|
||||
|
||||
const restAPIVisible =
|
||||
|
|
@ -380,7 +383,7 @@ const mapStateToProps = (
|
|||
|
||||
return {
|
||||
plugins,
|
||||
premiumPlugins,
|
||||
upcomingIntegrations,
|
||||
authApiPlugin,
|
||||
restAPIVisible,
|
||||
graphQLAPIVisible,
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {
|
|||
} from "ee/constants/messages";
|
||||
import { useSelector } from "react-redux";
|
||||
import { pluginSearchSelector } from "./CreateNewDatasourceHeader";
|
||||
import { getPlugins } from "ee/selectors/entitiesSelector";
|
||||
import { getFilteredPremiumIntegrations } from "./PremiumDatasources/Constants";
|
||||
import { getPlugins, getUpcomingPlugins } from "ee/selectors/entitiesSelector";
|
||||
import { getFilteredUpcomingIntegrations } from "./PremiumDatasources/Constants";
|
||||
import styled from "styled-components";
|
||||
import { filterSearch } from "./util";
|
||||
import type { MockDatasource } from "entities/Datasource";
|
||||
|
|
@ -34,6 +34,8 @@ export default function EmptySearchedPlugins({
|
|||
pluginSearchSelector(state, "search"),
|
||||
);
|
||||
|
||||
const upcomingPlugins = useSelector(getUpcomingPlugins);
|
||||
|
||||
searchedPlugin = (searchedPlugin || "").toLocaleLowerCase();
|
||||
const plugins = useSelector(getPlugins);
|
||||
|
||||
|
|
@ -59,9 +61,10 @@ export default function EmptySearchedPlugins({
|
|||
{ name: createMessage(CREATE_NEW_DATASOURCE_AUTHENTICATED_REST_API) },
|
||||
...mockDatasources,
|
||||
...(isPremiumDatasourcesViewEnabled
|
||||
? getFilteredPremiumIntegrations(
|
||||
? getFilteredUpcomingIntegrations(
|
||||
isExternalSaasEnabled || isIntegrationsEnabledForPaid,
|
||||
pluginNames,
|
||||
upcomingPlugins,
|
||||
)
|
||||
: []),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,40 +1,24 @@
|
|||
import { getAssetUrl } from "ee/utils/airgapHelpers";
|
||||
import { ASSETS_CDN_URL } from "../../../../constants/ThirdPartyConstants";
|
||||
import type { UpcomingIntegration } from "entities/Plugin";
|
||||
|
||||
export interface PremiumIntegration {
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const PREMIUM_INTEGRATIONS: PremiumIntegration[] = [
|
||||
{
|
||||
name: "Zendesk",
|
||||
icon: getAssetUrl(`${ASSETS_CDN_URL}/zendesk-icon.png`),
|
||||
},
|
||||
{
|
||||
name: "Salesforce",
|
||||
icon: getAssetUrl(`${ASSETS_CDN_URL}/salesforce-image.png`),
|
||||
},
|
||||
{
|
||||
name: "Slack",
|
||||
icon: getAssetUrl(`${ASSETS_CDN_URL}/slack.png`),
|
||||
},
|
||||
{
|
||||
name: "Jira",
|
||||
icon: getAssetUrl(`${ASSETS_CDN_URL}/jira.png`),
|
||||
},
|
||||
];
|
||||
|
||||
export const getFilteredPremiumIntegrations = (
|
||||
/**
|
||||
* Filters upcoming integrations based on available plugins.
|
||||
* Returns cached integrations synchronously.
|
||||
*
|
||||
* @param isExternalSaasEnabled Whether external SaaS integrations are enabled
|
||||
* @param pluginNames List of installed plugin names (lowercase)
|
||||
* @returns Filtered list of upcoming integrations
|
||||
*/
|
||||
export const getFilteredUpcomingIntegrations = (
|
||||
isExternalSaasEnabled: boolean,
|
||||
pluginNames: string[],
|
||||
) => {
|
||||
upcomingPlugins: UpcomingIntegration[],
|
||||
): UpcomingIntegration[] => {
|
||||
return isExternalSaasEnabled
|
||||
? PREMIUM_INTEGRATIONS.filter(
|
||||
? upcomingPlugins.filter(
|
||||
(integration) =>
|
||||
!pluginNames.includes(integration.name.toLocaleLowerCase()),
|
||||
)
|
||||
: PREMIUM_INTEGRATIONS;
|
||||
: upcomingPlugins;
|
||||
};
|
||||
|
||||
export const PREMIUM_INTEGRATION_CONTACT_FORM =
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import styled from "styled-components";
|
|||
import ContactForm from "./ContactForm";
|
||||
import { handlePremiumDatasourceClick } from "./Helpers";
|
||||
import DatasourceItem from "../DatasourceItem";
|
||||
import type { PremiumIntegration } from "./Constants";
|
||||
import { createMessage } from "ee/constants/messages";
|
||||
import { PREMIUM_DATASOURCES } from "ee/constants/messages";
|
||||
import type { UpcomingIntegration } from "entities/Plugin";
|
||||
|
||||
const ModalContentWrapper = styled(ModalContent)`
|
||||
max-width: 518px;
|
||||
|
|
@ -26,7 +26,7 @@ const PremiumTag = styled(Tag)`
|
|||
`;
|
||||
|
||||
export default function PremiumDatasources(props: {
|
||||
plugins: PremiumIntegration[];
|
||||
plugins: UpcomingIntegration[];
|
||||
isIntegrationsEnabledForPaid?: boolean;
|
||||
}) {
|
||||
const [selectedIntegration, setSelectedIntegration] = useState<string>("");
|
||||
|
|
@ -49,7 +49,7 @@ export default function PremiumDatasources(props: {
|
|||
handleOnClick={() => {
|
||||
handleOnClick(integration.name);
|
||||
}}
|
||||
icon={getAssetUrl(integration.icon)}
|
||||
icon={getAssetUrl(integration.iconLocation)}
|
||||
key={integration.name}
|
||||
name={integration.name}
|
||||
rightSibling={
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import {
|
|||
ReduxActionTypes,
|
||||
ReduxActionErrorTypes,
|
||||
} from "ee/constants/ReduxActionConstants";
|
||||
import type { DefaultPlugin, Plugin } from "entities/Plugin";
|
||||
import type {
|
||||
DefaultPlugin,
|
||||
Plugin,
|
||||
UpcomingIntegration,
|
||||
} from "entities/Plugin";
|
||||
import type {
|
||||
PluginFormPayloadWithId,
|
||||
PluginFormsPayload,
|
||||
|
|
@ -30,6 +34,10 @@ export interface PluginDataState {
|
|||
datasourceFormButtonConfigs: FormDatasourceButtonConfigs;
|
||||
fetchingSinglePluginForm: Record<string, boolean>;
|
||||
fetchingDefaultPlugins: boolean;
|
||||
upcomingPlugins: {
|
||||
list: UpcomingIntegration[];
|
||||
loading: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const initialState: PluginDataState = {
|
||||
|
|
@ -43,6 +51,10 @@ const initialState: PluginDataState = {
|
|||
dependencies: {},
|
||||
fetchingSinglePluginForm: {},
|
||||
fetchingDefaultPlugins: false,
|
||||
upcomingPlugins: {
|
||||
list: [],
|
||||
loading: false,
|
||||
},
|
||||
};
|
||||
|
||||
const pluginsReducer = createReducer(initialState, {
|
||||
|
|
@ -142,6 +154,33 @@ const pluginsReducer = createReducer(initialState, {
|
|||
defaultPluginList: action.payload,
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.GET_UPCOMING_PLUGINS_REQUEST]: (state: PluginDataState) => {
|
||||
return {
|
||||
...state,
|
||||
upcomingPlugins: { ...state.upcomingPlugins, loading: true },
|
||||
};
|
||||
},
|
||||
[ReduxActionTypes.GET_UPCOMING_PLUGINS_SUCCESS]: (
|
||||
state: PluginDataState,
|
||||
action: ReduxAction<UpcomingIntegration[]>,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
upcomingPlugins: {
|
||||
...state.upcomingPlugins,
|
||||
loading: false,
|
||||
list: action.payload,
|
||||
},
|
||||
};
|
||||
},
|
||||
[ReduxActionErrorTypes.GET_UPCOMING_PLUGINS_ERROR]: (
|
||||
state: PluginDataState,
|
||||
) => {
|
||||
return {
|
||||
...state,
|
||||
upcomingPlugins: { ...state.upcomingPlugins, loading: false },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default pluginsReducer;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,12 @@ import type { ApiResponse } from "api/ApiResponses";
|
|||
import PluginApi from "api/PluginApi";
|
||||
import log from "loglevel";
|
||||
import { getAppsmithAIPlugin, getGraphQLPlugin } from "entities/Action";
|
||||
import { type DefaultPlugin, type Plugin, PluginType } from "entities/Plugin";
|
||||
import {
|
||||
type DefaultPlugin,
|
||||
type Plugin,
|
||||
PluginType,
|
||||
type UpcomingIntegration,
|
||||
} from "entities/Plugin";
|
||||
import type {
|
||||
FormEditorConfigs,
|
||||
FormSettingsConfigs,
|
||||
|
|
@ -305,6 +310,24 @@ function* getDefaultPluginsSaga() {
|
|||
}
|
||||
}
|
||||
|
||||
function* getUpcomingPluginsSaga() {
|
||||
try {
|
||||
const response: ApiResponse<UpcomingIntegration[]> = yield call(
|
||||
PluginsApi.fetchUpcomingIntegrations,
|
||||
);
|
||||
|
||||
yield put({
|
||||
type: ReduxActionTypes.GET_UPCOMING_PLUGINS_SUCCESS,
|
||||
payload: response.data || [],
|
||||
});
|
||||
} catch (error) {
|
||||
yield put({
|
||||
type: ReduxActionErrorTypes.GET_UPCOMING_PLUGINS_ERROR,
|
||||
payload: { error },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function* root() {
|
||||
yield all([
|
||||
takeEvery(ReduxActionTypes.FETCH_PLUGINS_REQUEST, fetchPluginsSaga),
|
||||
|
|
@ -320,6 +343,10 @@ function* root() {
|
|||
ReduxActionTypes.GET_DEFAULT_PLUGINS_REQUEST,
|
||||
getDefaultPluginsSaga,
|
||||
),
|
||||
takeEvery(
|
||||
ReduxActionTypes.GET_UPCOMING_PLUGINS_REQUEST,
|
||||
getUpcomingPluginsSaga,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user