diff --git a/app/client/src/components/editorComponents/ActionCreator/index.tsx b/app/client/src/components/editorComponents/ActionCreator/index.tsx
index 257e726115..43d342d5f0 100644
--- a/app/client/src/components/editorComponents/ActionCreator/index.tsx
+++ b/app/client/src/components/editorComponents/ActionCreator/index.tsx
@@ -391,7 +391,8 @@ function getIntegrationOptionsWithChildren(
const apis = actions.filter(
(action) =>
action.config.pluginType === PluginType.API ||
- action.config.pluginType === PluginType.SAAS,
+ action.config.pluginType === PluginType.SAAS ||
+ action.config.pluginType === PluginType.REMOTE,
);
const option = options.find(
(option) => option.value === ActionType.integration,
diff --git a/app/client/src/components/editorComponents/CodeEditor/generateQuickCommands.tsx b/app/client/src/components/editorComponents/CodeEditor/generateQuickCommands.tsx
index 10d90fb318..801b81ff94 100644
--- a/app/client/src/components/editorComponents/CodeEditor/generateQuickCommands.tsx
+++ b/app/client/src/components/editorComponents/CodeEditor/generateQuickCommands.tsx
@@ -104,6 +104,7 @@ function Command(props: {
DB: ,
API: ,
SAAS: ,
+ REMOTE: ,
JS: ,
}[props.pluginType]}
{props.imgSrc &&
}
diff --git a/app/client/src/components/editorComponents/LightningMenu/hooks.ts b/app/client/src/components/editorComponents/LightningMenu/hooks.ts
index 96eaf0aec2..4b7e5a7030 100644
--- a/app/client/src/components/editorComponents/LightningMenu/hooks.ts
+++ b/app/client/src/components/editorComponents/LightningMenu/hooks.ts
@@ -30,7 +30,11 @@ export const useActions = () => {
.map((action) => action.config);
const saas: Action[] = actions
- .filter((action) => action.config.pluginType === PluginType.SAAS)
+ .filter(
+ (action) =>
+ action.config.pluginType === PluginType.SAAS ||
+ action.config.pluginType === PluginType.REMOTE,
+ )
.map((action) => action.config);
return { apis, queries, saas };
diff --git a/app/client/src/constants/AppsmithActionConstants/ActionConstants.tsx b/app/client/src/constants/AppsmithActionConstants/ActionConstants.tsx
index e1ea8b925d..0d3586fbdb 100644
--- a/app/client/src/constants/AppsmithActionConstants/ActionConstants.tsx
+++ b/app/client/src/constants/AppsmithActionConstants/ActionConstants.tsx
@@ -119,6 +119,7 @@ export const defaultActionSettings: Record = {
[PluginType.API]: apiActionSettingsConfig,
[PluginType.DB]: queryActionSettingsConfig,
[PluginType.SAAS]: saasActionSettingsConfig,
+ [PluginType.REMOTE]: saasActionSettingsConfig,
[PluginType.JS]: [],
};
@@ -126,6 +127,7 @@ export const defaultActionEditorConfigs: Record = {
[PluginType.API]: apiActionEditorConfig,
[PluginType.DB]: [],
[PluginType.SAAS]: [],
+ [PluginType.REMOTE]: [],
[PluginType.JS]: [],
};
@@ -136,5 +138,6 @@ export const defaultActionDependenciesConfig: Record<
[PluginType.API]: apiActionDependencyConfig,
[PluginType.DB]: {},
[PluginType.SAAS]: {},
+ [PluginType.REMOTE]: {},
[PluginType.JS]: {},
};
diff --git a/app/client/src/entities/Action/index.ts b/app/client/src/entities/Action/index.ts
index c4761a707c..24040b6e9e 100644
--- a/app/client/src/entities/Action/index.ts
+++ b/app/client/src/entities/Action/index.ts
@@ -7,6 +7,7 @@ export enum PluginType {
DB = "DB",
SAAS = "SAAS",
JS = "JS",
+ REMOTE = "REMOTE",
}
export enum PaginationType {
@@ -93,6 +94,11 @@ export interface SaaSAction extends BaseAction {
actionConfiguration: any;
datasource: StoredDatasource;
}
+export interface RemoteAction extends BaseAction {
+ pluginType: PluginType.REMOTE;
+ actionConfiguration: any;
+ datasource: StoredDatasource;
+}
export interface EmbeddedApiAction extends BaseApiAction {
datasource: EmbeddedRestDatasource;
@@ -127,4 +133,4 @@ export type ActionViewMode = {
timeoutInMillisecond?: number;
};
-export type Action = ApiAction | QueryAction | SaaSAction;
+export type Action = ApiAction | QueryAction | SaaSAction | RemoteAction;
diff --git a/app/client/src/pages/Editor/Explorer/Actions/helpers.tsx b/app/client/src/pages/Editor/Explorer/Actions/helpers.tsx
index 13d4c48eed..922aacc688 100644
--- a/app/client/src/pages/Editor/Explorer/Actions/helpers.tsx
+++ b/app/client/src/pages/Editor/Explorer/Actions/helpers.tsx
@@ -55,7 +55,7 @@ export type ActionGroupConfig = {
export const ACTION_PLUGIN_MAP: Array = [
{
groupName: "Datasources",
- types: [PluginType.API, PluginType.SAAS, PluginType.DB],
+ types: [PluginType.API, PluginType.SAAS, PluginType.DB, PluginType.REMOTE],
icon: dbQueryIcon,
key: generateReactKey(),
getURL: (
@@ -72,7 +72,10 @@ export const ACTION_PLUGIN_MAP: Array = [
plugin.packageName,
id,
)}`;
- } else if (pluginType === PluginType.DB) {
+ } else if (
+ pluginType === PluginType.DB ||
+ pluginType === PluginType.REMOTE
+ ) {
return `${QUERIES_EDITOR_ID_URL(applicationId, pageId, id)}`;
} else {
return `${API_EDITOR_ID_URL(applicationId, pageId, id)}`;
@@ -164,6 +167,9 @@ export const getPluginGroups = (
...entries.filter(
(entry: any) => entry.config.pluginType === PluginType.DB,
),
+ ...entries.filter(
+ (entry: any) => entry.config.pluginType === PluginType.REMOTE,
+ ),
]
: entries;
diff --git a/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx b/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx
index 514342e691..1f164bd5f0 100644
--- a/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx
+++ b/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx
@@ -294,7 +294,9 @@ function NewApiScreen(props: Props) {
)}
{plugins
- .filter((p) => p.type === PluginType.SAAS)
+ .filter(
+ (p) => p.type === PluginType.SAAS || p.type === PluginType.REMOTE,
+ )
.map((p) => (
{
plugins: allPlugins,
runErrorMessage,
pluginIds: getPluginIdsOfPackageNames(state, PLUGIN_PACKAGE_DBS),
- dataSources: getDBDatasources(state),
+ dataSources: getDBAndRemoteDatasources(state),
responses: getActionResponses(state),
isRunning: state.ui.queryPane.isRunning[props.match.params.queryId],
isDeleting: state.ui.queryPane.isDeleting[props.match.params.queryId],
diff --git a/app/client/src/sagas/QueryPaneSagas.ts b/app/client/src/sagas/QueryPaneSagas.ts
index 9bb76942c0..0f64b91d2f 100644
--- a/app/client/src/sagas/QueryPaneSagas.ts
+++ b/app/client/src/sagas/QueryPaneSagas.ts
@@ -160,7 +160,7 @@ function* handleQueryCreatedSaga(actionPayload: ReduxAction) {
pluginId,
pluginType,
} = actionPayload.payload;
- if (pluginType === PluginType.DB) {
+ if (pluginType === PluginType.DB || pluginType === PluginType.REMOTE) {
yield put(initialize(QUERY_EDITOR_FORM_NAME, actionPayload.payload));
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
@@ -182,8 +182,10 @@ function* handleQueryCreatedSaga(actionPayload: ReduxAction) {
function* handleDatasourceCreatedSaga(actionPayload: ReduxAction) {
const plugin = yield select(getPlugin, actionPayload.payload.pluginId);
+ debugger;
// Only look at db plugins
- if (plugin.type !== PluginType.DB) return;
+ if (plugin.type !== PluginType.DB && plugin.type !== PluginType.REMOTE)
+ return;
const applicationId = yield select(getCurrentApplicationId);
const pageId = yield select(getCurrentPageId);
diff --git a/app/client/src/selectors/entitiesSelector.ts b/app/client/src/selectors/entitiesSelector.ts
index 180bad80e3..ac21aea257 100644
--- a/app/client/src/selectors/entitiesSelector.ts
+++ b/app/client/src/selectors/entitiesSelector.ts
@@ -181,6 +181,13 @@ export const getDBPlugins = createSelector(getPlugins, (plugins) =>
plugins.filter((plugin) => plugin.type === PluginType.DB),
);
+export const getDBAndRemotePlugins = createSelector(getPlugins, (plugins) =>
+ plugins.filter(
+ (plugin) =>
+ plugin.type === PluginType.DB || plugin.type === PluginType.REMOTE,
+ ),
+);
+
export const getDatasourceByPluginId = (state: AppState, pluginId: string) =>
state.entities.datasources.list.filter((d) => d.pluginId === pluginId);
@@ -197,6 +204,19 @@ export const getDBDatasources = createSelector(
},
);
+export const getDBAndRemoteDatasources = createSelector(
+ getDBAndRemotePlugins,
+ getEntities,
+ (plugins, entities) => {
+ const datasources = entities.datasources.list;
+ const pluginIds = plugins.map((plugin) => plugin.id);
+
+ return datasources.filter((datasource) =>
+ pluginIds.includes(datasource.pluginId),
+ );
+ },
+);
+
export const getQueryName = (state: AppState, actionId: string): string => {
const action = state.entities.actions.find((action: ActionData) => {
return action.config.id === actionId;
diff --git a/app/server/appsmith-interfaces/pom.xml b/app/server/appsmith-interfaces/pom.xml
index 944db31b54..61b59468c2 100644
--- a/app/server/appsmith-interfaces/pom.xml
+++ b/app/server/appsmith-interfaces/pom.xml
@@ -131,6 +131,10 @@
h2
1.4.200
+
+ org.springframework
+ spring-webflux
+
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/DatasourceDTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/DatasourceDTO.java
new file mode 100644
index 0000000000..58416df76c
--- /dev/null
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/DatasourceDTO.java
@@ -0,0 +1,14 @@
+package com.appsmith.external.dtos;
+
+import com.appsmith.external.models.DatasourceConfiguration;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class DatasourceDTO {
+ String id;
+ DatasourceConfiguration datasourceConfiguration;
+}
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/ExecutePluginDTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/ExecutePluginDTO.java
new file mode 100644
index 0000000000..a5ae2ff669
--- /dev/null
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/ExecutePluginDTO.java
@@ -0,0 +1,18 @@
+package com.appsmith.external.dtos;
+
+import com.appsmith.external.models.ActionConfiguration;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ExecutePluginDTO {
+ String installationKey;
+ String pluginName;
+ String pluginVersion;
+ String actionTemplateName;
+ String datasourceTemplateName;
+ DatasourceDTO datasource;
+ ActionConfiguration actionConfiguration;
+ ExecuteActionDTO executeActionDTO;
+}
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ActionConfiguration.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ActionConfiguration.java
index fdc7fd8e65..942aac80b8 100644
--- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ActionConfiguration.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/ActionConfiguration.java
@@ -5,6 +5,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.Range;
+import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.http.HttpMethod;
@@ -75,6 +76,9 @@ public class ActionConfiguration implements AppsmithDomain {
*/
Map formData;
+ @Transient
+ String templateName;
+
public void setTimeoutInMillisecond(String timeoutInMillisecond) {
try {
this.timeoutInMillisecond = Integer.valueOf(timeoutInMillisecond);
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java
index 423da04b3f..7002d5b535 100644
--- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java
@@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
-import org.springframework.data.annotation.Transient;
import reactor.core.publisher.Mono;
import java.util.Set;
@@ -24,7 +23,6 @@ import java.util.Set;
@JsonSubTypes.Type(value = DBAuth.class, name = Authentication.DB_AUTH),
@JsonSubTypes.Type(value = OAuth2.class, name = Authentication.OAUTH2),
@JsonSubTypes.Type(value = BasicAuth.class, name = Authentication.BASIC),
- @JsonSubTypes.Type(value = BasicAuth.class, name = Authentication.BASIC),
@JsonSubTypes.Type(value = ApiKeyAuth.class, name = Authentication.API_KEY),
@JsonSubTypes.Type(value = BearerTokenAuth.class, name = Authentication.BEARER_TOKEN)
})
@@ -39,7 +37,6 @@ public class AuthenticationDTO implements AppsmithDomain {
SUCCESS
};
- @Transient
String authenticationType;
AuthenticationStatus authenticationStatus;
diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Datasource.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Datasource.java
similarity index 93%
rename from app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Datasource.java
rename to app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Datasource.java
index 3c5919c9b9..a874e082c3 100644
--- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Datasource.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Datasource.java
@@ -1,8 +1,5 @@
-package com.appsmith.server.domains;
+package com.appsmith.external.models;
-import com.appsmith.external.models.BaseDomain;
-import com.appsmith.external.models.DatasourceConfiguration;
-import com.appsmith.external.models.DatasourceStructure;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
@@ -32,6 +29,8 @@ public class Datasource extends BaseDomain {
String organizationId;
+ String templateName;
+
DatasourceConfiguration datasourceConfiguration;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java
index d04c1bf71c..7de665abfb 100644
--- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java
@@ -37,8 +37,10 @@ public class OAuth2 extends AuthenticationDTO {
Type grantType;
+ // Send tokens as query params if false
Boolean isTokenHeader = false;
+ // Send auth details in body if false
Boolean isAuthorizationHeader = false;
String clientId;
diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/SharedConfig.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/SharedConfig.java
index 8f1eaa630a..8774fcda7b 100644
--- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/SharedConfig.java
+++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/SharedConfig.java
@@ -5,4 +5,6 @@ public interface SharedConfig {
int getCodecSize();
int getMaxResponseSize();
+
+ String getRemoteExecutionUrl();
}
diff --git a/app/server/appsmith-plugins/pom.xml b/app/server/appsmith-plugins/pom.xml
index 1daab7260e..8a60d6026a 100644
--- a/app/server/appsmith-plugins/pom.xml
+++ b/app/server/appsmith-plugins/pom.xml
@@ -30,5 +30,6 @@
snowflakePlugin
arangoDBPlugin
jsPlugin
+ saasPlugin
\ No newline at end of file
diff --git a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java
index a9490fca25..f93c558c0e 100644
--- a/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java
+++ b/app/server/appsmith-plugins/postgresPlugin/src/test/java/com/external/plugins/PostgresPluginTest.java
@@ -65,6 +65,11 @@ public class PostgresPluginTest {
public int getMaxResponseSize() {
return 10000;
}
+
+ @Override
+ public String getRemoteExecutionUrl() {
+ return "";
+ }
}
diff --git a/app/server/appsmith-plugins/restApiPlugin/src/test/java/com/external/plugins/RestApiPluginTest.java b/app/server/appsmith-plugins/restApiPlugin/src/test/java/com/external/plugins/RestApiPluginTest.java
index afcdda0b69..e39eeaa174 100644
--- a/app/server/appsmith-plugins/restApiPlugin/src/test/java/com/external/plugins/RestApiPluginTest.java
+++ b/app/server/appsmith-plugins/restApiPlugin/src/test/java/com/external/plugins/RestApiPluginTest.java
@@ -57,6 +57,11 @@ public class RestApiPluginTest {
public int getMaxResponseSize() {
return 10000;
}
+
+ @Override
+ public String getRemoteExecutionUrl() {
+ return "";
+ }
}
RestApiPlugin.RestApiPluginExecutor pluginExecutor = new RestApiPlugin.RestApiPluginExecutor(new MockSharedConfig());
diff --git a/app/server/appsmith-plugins/saasPlugin/plugin.properties b/app/server/appsmith-plugins/saasPlugin/plugin.properties
new file mode 100644
index 0000000000..198f0f4feb
--- /dev/null
+++ b/app/server/appsmith-plugins/saasPlugin/plugin.properties
@@ -0,0 +1,5 @@
+plugin.id=saas-plugin
+plugin.class=com.external.plugins.SaasPlugin
+plugin.version=1.0-SNAPSHOT
+plugin.provider=tech@appsmith.com
+plugin.dependencies=
diff --git a/app/server/appsmith-plugins/saasPlugin/pom.xml b/app/server/appsmith-plugins/saasPlugin/pom.xml
new file mode 100644
index 0000000000..cef34082a4
--- /dev/null
+++ b/app/server/appsmith-plugins/saasPlugin/pom.xml
@@ -0,0 +1,120 @@
+
+
+
+ 4.0.0
+
+ com.external.plugins
+ saasPlugin
+ 1.0-SNAPSHOT
+
+ saasPlugin
+
+
+ UTF-8
+ 11
+ ${java.version}
+ ${java.version}
+ saas-plugin
+ com.external.plugins.SaasPlugin
+ 1.0-SNAPSHOT
+ tech@appsmith.com
+
+
+
+
+
+
+ org.pf4j
+ pf4j-spring
+ 0.7.0
+ provided
+
+
+
+ com.appsmith
+ interfaces
+ 1.0-SNAPSHOT
+ provided
+
+
+
+ org.springframework
+ spring-core
+ 5.2.3.RELEASE
+ provided
+
+
+
+ org.springframework
+ spring-web
+ 5.2.3.RELEASE
+ provided
+
+
+
+ org.springframework
+ spring-webflux
+ 5.2.3.RELEASE
+
+
+ io.projectreactor
+ reactor-core
+
+
+ org.springframework
+ spring-core
+
+
+ org.springframework
+ spring-web
+
+
+
+
+ org.projectlombok
+ lombok
+ RELEASE
+ compile
+
+
+ org.projectlombok
+ lombok
+ RELEASE
+ compile
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+ false
+
+
+
+ ${plugin.id}
+ ${plugin.class}
+ ${plugin.version}
+ ${plugin.provider}
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
diff --git a/app/server/appsmith-plugins/saasPlugin/src/main/java/com/external/helpers/BodyReceiver.java b/app/server/appsmith-plugins/saasPlugin/src/main/java/com/external/helpers/BodyReceiver.java
new file mode 100644
index 0000000000..a84b90a847
--- /dev/null
+++ b/app/server/appsmith-plugins/saasPlugin/src/main/java/com/external/helpers/BodyReceiver.java
@@ -0,0 +1,228 @@
+package com.external.helpers;
+
+import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
+import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import org.springframework.core.ResolvableType;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.http.HttpCookie;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ReactiveHttpOutputMessage;
+import org.springframework.http.client.reactive.ClientHttpRequest;
+import org.springframework.http.codec.HttpMessageWriter;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserter;
+import reactor.core.publisher.Mono;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * This receiver essentially instantiates a custom ClientHttpRequest that stores the request body via a subscriber
+ * The BodyInserter instance from our original request inserts into this subscriber that we can then retrieve
+ * using receiveValue.
+ *
+ * We do this so that the received value that we display to the user is exactly the same as
+ * the body tht is sent over the wire
+ */
+public class BodyReceiver {
+ private static final Object DUMMY = new Object();
+
+ private final AtomicReference