feat: Added new api to fetch preview data (#26298)

This commit is contained in:
sneha122 2023-08-22 17:32:01 +05:30 committed by GitHub
parent 6b239561da
commit 5b609e0e8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 1 deletions

View File

@ -155,6 +155,24 @@ public enum AppsmithPluginError implements BasePluginError {
ErrorType.INTERNAL_ERROR,
"{1}",
"{2}"),
PLUGIN_GET_PREVIEW_DATA_ERROR(
500,
AppsmithPluginErrorCode.PLUGIN_GET_PREVIEW_DATA_ERROR.getCode(),
AppsmithPluginErrorCode.PLUGIN_GET_PREVIEW_DATA_ERROR.getDescription(),
AppsmithErrorAction.DEFAULT,
"Failed to get preview data",
ErrorType.DATASOURCE_CONFIGURATION_ERROR,
"{0}",
"{1}"),
PLUGIN_UNSUPPORTED_OPERATION(
500,
AppsmithPluginErrorCode.PLUGIN_UNSUPPORTED_OPERATION.getCode(),
AppsmithPluginErrorCode.PLUGIN_UNSUPPORTED_OPERATION.getDescription(),
AppsmithErrorAction.DEFAULT,
"Unsupported Operation",
ErrorType.INTERNAL_ERROR,
"{0}",
"{1}"),
;
private final Integer httpErrorCode;

View File

@ -20,7 +20,9 @@ public enum AppsmithPluginErrorCode {
PLUGIN_UQI_WHERE_CONDITION_UNKNOWN("PE-UQI-5000", "Where condition could not be parsed"),
GENERIC_STALE_CONNECTION("PE-STC-5000", "Secondary stale connection error"),
PLUGIN_EXECUTE_ARGUMENT_ERROR("PE-ARG-5000", "Wrong arguments provided"),
PLUGIN_VALIDATE_DATASOURCE_ERROR("PE-DSE-5005", "Failed to validate datasource");
PLUGIN_VALIDATE_DATASOURCE_ERROR("PE-DSE-5005", "Failed to validate datasource"),
PLUGIN_GET_PREVIEW_DATA_ERROR("PE-DSE-5006", "Failed to get preview data"),
PLUGIN_UNSUPPORTED_OPERATION("PE-DSE-5007", "Unsupported Operation");
private final String code;
private final String description;

View File

@ -8,6 +8,7 @@ import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Template;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.Param;
import com.appsmith.external.models.TriggerRequestDTO;
@ -325,4 +326,12 @@ public interface PluginExecutor<C> extends ExtensionPoint, CrudTemplateService {
List<ActionConfiguration> actionConfigurationList, Object... args) {
return Mono.empty();
}
/*
* This method returns ActionConfiguration required in order to fetch preview data,
* that needs to be shown on datasource review page.
*/
default ActionConfiguration getSchemaPreviewActionConfig(Template queryTemplate) {
return null;
}
}

View File

@ -14,6 +14,7 @@ import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Template;
import com.appsmith.external.models.Endpoint;
import com.appsmith.external.models.MustacheBindingToken;
import com.appsmith.external.models.Param;
@ -60,6 +61,7 @@ import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
@ -268,6 +270,21 @@ public class PostgresPlugin extends BasePlugin {
explicitCastDataTypes);
}
@Override
public ActionConfiguration getSchemaPreviewActionConfig(Template queryTemplate) {
ActionConfiguration actionConfig = new ActionConfiguration();
// Sets query body
actionConfig.setBody(queryTemplate.getBody());
// Sets prepared statement to false
Property preparedStatement = new Property();
preparedStatement.setValue(false);
List<Property> pluginSpecifiedTemplates = new ArrayList<Property>();
pluginSpecifiedTemplates.add(preparedStatement);
actionConfig.setPluginSpecifiedTemplates(pluginSpecifiedTemplates);
return actionConfig;
}
private Mono<ActionExecutionResult> executeCommon(
HikariDataSource connection,
DatasourceConfiguration datasourceConfiguration,
@ -285,6 +302,7 @@ public class PostgresPlugin extends BasePlugin {
String transformedQuery = preparedStatement ? replaceQuestionMarkWithDollarIndex(query) : query;
List<RequestParamDTO> requestParams =
List.of(new RequestParamDTO(ACTION_CONFIGURATION_BODY, transformedQuery, null, null, psParams));
Instant requestedAt = Instant.now();
return Mono.fromCallable(() -> {
Connection connectionFromPool;
@ -544,6 +562,9 @@ public class PostgresPlugin extends BasePlugin {
request.setQuery(query);
request.setProperties(requestData);
request.setRequestParams(requestParams);
if (request.getRequestedAt() == null) {
request.setRequestedAt(requestedAt);
}
ActionExecutionResult result = actionExecutionResult;
result.setRequest(request);
return result;

View File

@ -1,8 +1,10 @@
package com.appsmith.server.controllers.ce;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceStorageDTO;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Template;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.TriggerRequestDTO;
import com.appsmith.external.models.TriggerResultDTO;
@ -206,4 +208,16 @@ public class DatasourceControllerCE {
.trigger(datasourceId, environmentId, triggerRequestDTO)
.map(triggerResultDTO -> new ResponseDTO<>(HttpStatus.OK.value(), triggerResultDTO, null));
}
@JsonView(Views.Public.class)
@PostMapping("/{datasourceId}/schema-preview")
public Mono<ResponseDTO<ActionExecutionResult>> getSchemaPreviewData(
@PathVariable String datasourceId,
@RequestBody Template template,
@RequestHeader(name = FieldName.ENVIRONMENT_ID, required = false) String environmentId) {
log.debug("Going to get schema preview data for datasource with id: '{}'.", datasourceId);
return datasourceStructureSolution
.getSchemaPreviewData(datasourceId, environmentId, template)
.map(actionExecutionResult -> new ResponseDTO<>(HttpStatus.OK.value(), actionExecutionResult, null));
}
}

View File

@ -1,7 +1,9 @@
package com.appsmith.server.solutions.ce;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DatasourceStorage;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Template;
import reactor.core.publisher.Mono;
public interface DatasourceStructureSolutionCE {
@ -9,4 +11,7 @@ public interface DatasourceStructureSolutionCE {
Mono<DatasourceStructure> getStructure(String datasourceId, boolean ignoreCache, String environmentName);
Mono<DatasourceStructure> getStructure(DatasourceStorage datasourceStorage, boolean ignoreCache);
Mono<ActionExecutionResult> getSchemaPreviewData(
String datasourceId, String environmentName, Template queryTemplate);
}

View File

@ -4,9 +4,13 @@ import com.appsmith.external.constants.AnalyticsEvents;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.DatasourceStorage;
import com.appsmith.external.models.DatasourceStorageStructure;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceStructure.Template;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.exceptions.AppsmithError;
@ -172,4 +176,80 @@ public class DatasourceStructureSolutionCEImpl implements DatasourceStructureSol
.switchIfEmpty(fetchAndStoreNewStructureMono)
.defaultIfEmpty(new DatasourceStructure());
}
@Override
public Mono<ActionExecutionResult> getSchemaPreviewData(
String datasourceId, String environmentId, Template queryTemplate) {
return datasourceService
.findById(datasourceId, datasourcePermission.getExecutePermission())
.zipWhen(datasource -> datasourceService.getTrueEnvironmentId(
datasource.getWorkspaceId(),
environmentId,
datasource.getPluginId(),
environmentPermission.getExecutePermission()))
.flatMap(tuple -> {
Datasource datasource = tuple.getT1();
String trueEnvironmentId = tuple.getT2();
return datasourceStorageService.findByDatasourceAndEnvironmentIdForExecution(
datasource, trueEnvironmentId);
})
.flatMap(datasourceStorage -> getSchemaPreviewData(datasourceStorage, queryTemplate))
.onErrorMap(e -> {
if (!(e instanceof AppsmithPluginException)) {
return new AppsmithPluginException(
AppsmithPluginError.PLUGIN_GET_PREVIEW_DATA_ERROR, e.getMessage());
}
return e;
})
.onErrorResume(error -> {
ActionExecutionResult result = new ActionExecutionResult();
result.setErrorInfo(error);
return Mono.just(result);
});
}
private Mono<ActionExecutionResult> getSchemaPreviewData(
DatasourceStorage datasourceStorage, Template queryTemplate) {
if (Boolean.FALSE.equals(datasourceStorage.getIsValid())) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_DATASOURCE));
}
return pluginExecutorHelper
.getPluginExecutor(pluginService.findById(datasourceStorage.getPluginId()))
.switchIfEmpty(Mono.error(new AppsmithException(
AppsmithError.NO_RESOURCE_FOUND, FieldName.PLUGIN, datasourceStorage.getPluginId())))
.flatMap(pluginExecutor -> {
ActionConfiguration actionConfig =
((PluginExecutor<Object>) pluginExecutor).getSchemaPreviewActionConfig(queryTemplate);
// actionConfig will be null for plugins which do not have this functionality yet
// Currently its only implemented for PostgreSQL, to be added subsequently for MySQL as well
if (actionConfig != null) {
return datasourceContextService.retryOnce(
datasourceStorage, resourceContext -> ((PluginExecutor<Object>) pluginExecutor)
.executeParameterized(
resourceContext.getConnection(),
null,
datasourceStorage.getDatasourceConfiguration(),
actionConfig));
} else {
return Mono.error(
new AppsmithPluginException(AppsmithPluginError.PLUGIN_UNSUPPORTED_OPERATION));
}
})
.onErrorMap(
StaleConnectionException.class,
error -> new AppsmithPluginException(
AppsmithPluginError.PLUGIN_ERROR,
"Appsmith server found a secondary stale connection. Please reach out to appsmith "
+ "customer support to resolve this."))
.onErrorMap(e -> {
log.error("In the datasourceStorage fetching preview data error mode.", e);
if (!(e instanceof AppsmithPluginException)) {
return new AppsmithPluginException(
AppsmithPluginError.PLUGIN_GET_PREVIEW_DATA_ERROR, e.getMessage());
}
return e;
});
}
}