From a272a0ec7a9f36954eae4f98e43c075fae375163 Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Thu, 9 Sep 2021 22:09:42 +0530 Subject: [PATCH] feat: Add consumption of UQI UI templates (#7082) * Same templates for mongo fetch * WIP * Working version * Minor refactoring and addition of comments for understanding the code flow * Minor fileName change * Extra check added to only try to read options for command --- .../server/exceptions/AppsmithError.java | 2 +- .../server/services/PluginService.java | 2 + .../server/services/PluginServiceImpl.java | 171 ++++++++++++++++-- 3 files changed, 157 insertions(+), 18 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java index 55a3eca89f..2a3e1120bf 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/AppsmithError.java @@ -86,7 +86,7 @@ public enum AppsmithError { PLUGIN_RUN_FAILED(500, 5003, "Plugin execution failed with error {0}", AppsmithErrorAction.DEFAULT, null, ErrorType.INTERNAL_ERROR), PLUGIN_EXECUTION_TIMEOUT(504, 5040, "Plugin Execution exceeded the maximum allowed time. Please increase the timeout in your action settings or check your backend action endpoint", AppsmithErrorAction.DEFAULT, null, ErrorType.CONNECTIVITY_ERROR), - PLUGIN_LOAD_FORM_JSON_FAIL(500, 5004, "Unable to load datasource form configuration. Details: {0}.", + PLUGIN_LOAD_FORM_JSON_FAIL(500, 5004, "[{0}] Unable to load datasource form configuration. Details: {1}.", AppsmithErrorAction.LOG_EXTERNALLY, null, ErrorType.INTERNAL_ERROR), PLUGIN_LOAD_TEMPLATES_FAIL(500, 5005, "Unable to load datasource templates. Details: {0}.", AppsmithErrorAction.LOG_EXTERNALLY, null, ErrorType.INTERNAL_ERROR), diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginService.java index d1c956a789..0a93b3e713 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginService.java @@ -33,4 +33,6 @@ public interface PluginService extends CrudService { Mono loadPluginResource(String pluginId, String resourcePath); Mono getEditorConfigLabelMap(String pluginId); + + Map loadEditorPluginResourceUqi(Plugin plugin); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java index 0e2b1ff9a7..e5e782fdd5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/PluginServiceImpl.java @@ -13,7 +13,11 @@ import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.repositories.PluginRepository; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; @@ -47,6 +51,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -56,6 +61,7 @@ import java.util.stream.Collectors; @Service public class PluginServiceImpl extends BaseService implements PluginService { + public static final String UQI_DB_EDITOR_FORM = "UQIDbEditorForm"; private final OrganizationService organizationService; private final PluginManager pluginManager; private final ReactiveRedisTemplate reactiveTemplate; @@ -68,12 +74,23 @@ public class PluginServiceImpl extends BaseService { + if (((Map) item).get(KEY_CHILDREN) == null) { + return false; + } + return true; + }) .map(item -> ((Map) item).get(KEY_CHILDREN)) .forEach(item -> ((List) item).stream() @@ -455,7 +478,7 @@ public class PluginServiceImpl extends BaseService it = commandsReadOnly.fieldNames(); it.hasNext(); ) { + String fieldName = it.next(); + JsonNode field = commandsReadOnly.get(fieldName); + // If this is a value node, today no manipulations are required. Copy as is to the writer node. + if (field.isValueNode()) { + commandWriterNode.put(fieldName, field); + } else if (field.isArray() && fieldName.equals(KEY_OPTIONS)) { + // Only array field present is options which stores all the command labels and fileNames for each label + try { + // Read all the command declarations in an array node. + ArrayNode commandTemplatesFromFile = (ArrayNode) objectMapper.readTree(String.valueOf(field)); + + for (JsonNode commandNode : commandTemplatesFromFile) { + ObjectNode commandOption = objectMapper.createObjectNode(); + JsonNode fileName = commandNode.get(KEY_FILE_NAME); + Map individualCommandMapReadOnly; + + // Only move forward if fileName is present. If not, this command declaration would be ignored + if (fileName != null) { + commandOption.set(KEY_LABEL, commandNode.get(KEY_LABEL)); + String path = UQI_QUERY_EDITOR_BASE_FOLDER + "/" + fileName.asText(); + try { + individualCommandMapReadOnly = loadPluginResourceGivenPlugin(plugin, path); + } catch (AppsmithException e) { + // Either the file doesnt exist or malformed JSON was found. Ignore the command template + log.error("Error loading resource JSON for plugin {} and resourcePath {} : ", plugin.getPackageName(), resourcePath, e); + continue; + } + + // Read the identified and if not present, again ignore the command template + Object identifierObj = individualCommandMapReadOnly.get(KEY_IDENTIFIER); + if (identifierObj != null) { + String identifier = (String) identifierObj; + commandOption.put(KEY_VALUE, identifier); + + // Only add the command in the final output in case of success + options.add(commandOption); + commandTemplates.add(objectMapper.valueToTree(individualCommandMapReadOnly)); + } + } + } + } catch (JsonProcessingException e) { + throw new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, plugin.getPackageName(), + "Error loading resource JSON for resourcePath " + resourcePath); + } + } + } + } + + ObjectNode topLevel = objectMapper.createObjectNode(); + ArrayNode editorOutput = topLevel.putArray("editor"); + editorOutput.add(commandWriterNode); + editorOutput.add(uiSectionNode); + + return objectMapper.convertValue(topLevel, new TypeReference>() { + }); + } + + private Map loadPluginResourceGivenPlugin(Plugin plugin, String resourcePath) { + InputStream resourceAsStream = pluginManager + .getPlugin(plugin.getPackageName()) + .getPluginClassLoader() + .getResourceAsStream(resourcePath); + + if (resourceAsStream == null) { + throw new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, plugin.getPackageName(), "form resource " + resourcePath + " not found"); + } + + try { + Map resourceMap = objectMapper.readValue(resourceAsStream, Map.class); + return resourceMap; + } catch (IOException e) { + log.error("[{}] : Error loading resource JSON for resourcePath {}", plugin.getPackageName(), resourcePath, e); + throw new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, plugin.getPackageName(), e.getMessage()); + } + } + @Override public Mono loadPluginResource(String pluginId, String resourcePath) { return findById(pluginId) - .flatMap(plugin -> { - InputStream resourceAsStream = pluginManager - .getPlugin(plugin.getPackageName()) - .getPluginClassLoader() - .getResourceAsStream(resourcePath); - - if (resourceAsStream == null) { - return Mono.error(new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, "form resource " + resourcePath + " not found")); - } - - try { - Map resourceMap = objectMapper.readValue(resourceAsStream, Map.class); - return Mono.just(resourceMap); - } catch (IOException e) { - log.error("Error loading resource JSON for pluginId {} and resourcePath {}", pluginId, resourcePath, e); - return Mono.error(new AppsmithException(AppsmithError.PLUGIN_LOAD_FORM_JSON_FAIL, e.getMessage())); + .map(plugin -> { + if (resourcePath.equals("editor.json") && plugin.getUiComponent().equals(UQI_DB_EDITOR_FORM)) { + return loadEditorPluginResourceUqi(plugin); } + return loadPluginResourceGivenPlugin(plugin, resourcePath); }); }