diff --git a/src/prerna/auth/utils/AbstractSecurityUtils.java b/src/prerna/auth/utils/AbstractSecurityUtils.java index b022a35041..fc88819269 100644 --- a/src/prerna/auth/utils/AbstractSecurityUtils.java +++ b/src/prerna/auth/utils/AbstractSecurityUtils.java @@ -929,12 +929,12 @@ public static void initialize() throws Exception { // Type and cost are the main questions - boolean projectExists = queryUtil.tableExists(conn, "PROJECT", database, schema); colNames = new String[] { "PROJECTID", "PROJECTNAME", "PROJECTDISPLAYNAME", "GLOBAL", "DISCOVERABLE", - "CREATEDBY", "CREATEDBYTYPE", "DATECREATED", "DATELASTEDITED", "TYPE", "COST", "CATALOGNAME", - "PORTALPUBLISHED", "PORTALPUBLISHEDUSER", "PORTALPUBLISHEDTYPE", "REACTORSCOMPILED", + "IS_TEMPLATE", "CREATEDBY", "CREATEDBYTYPE", "DATECREATED", "DATELASTEDITED", "TYPE", "COST", + "CATALOGNAME", "PORTALPUBLISHED", "PORTALPUBLISHEDUSER", "PORTALPUBLISHEDTYPE", "REACTORSCOMPILED", "REACTORSCOMPILEDUSER", "REACTORSCOMPILEDTYPE" }; types = new String[] { VARCHAR_255, VARCHAR_255, VARCHAR_255, BOOLEAN_DATATYPE_NAME, BOOLEAN_DATATYPE_NAME, - VARCHAR_255, VARCHAR_255, TIMESTAMP_DATATYPE_NAME, TIMESTAMP_DATATYPE_NAME, VARCHAR_255, - VARCHAR_255, VARCHAR_255, TIMESTAMP_DATATYPE_NAME, VARCHAR_255, VARCHAR_255, + BOOLEAN_DATATYPE_NAME, VARCHAR_255, VARCHAR_255, TIMESTAMP_DATATYPE_NAME, TIMESTAMP_DATATYPE_NAME, + VARCHAR_255, VARCHAR_255, VARCHAR_255, TIMESTAMP_DATATYPE_NAME, VARCHAR_255, VARCHAR_255, TIMESTAMP_DATATYPE_NAME, VARCHAR_255, VARCHAR_255 }; if (allowIfExistsTable) { String sql = queryUtil.createTableIfNotExists("PROJECT", colNames, types); @@ -966,6 +966,7 @@ public static void initialize() throws Exception { // backfill display name from canonical name for existing rows securityDb.insertData( "UPDATE PROJECT SET PROJECTDISPLAYNAME = PROJECTNAME WHERE PROJECTDISPLAYNAME IS NULL OR PROJECTDISPLAYNAME = ''"); + securityDb.insertData("UPDATE PROJECT SET IS_TEMPLATE = FALSE WHERE IS_TEMPLATE IS NULL"); } if (allowIfExistsIndexs) { String sql = queryUtil.createIndexIfNotExists("PROJECT_GLOBAL_INDEX", "PROJECT", "GLOBAL"); diff --git a/src/prerna/auth/utils/SecurityAdminUtils.java b/src/prerna/auth/utils/SecurityAdminUtils.java index ac4ebe2331..023776b1d5 100644 --- a/src/prerna/auth/utils/SecurityAdminUtils.java +++ b/src/prerna/auth/utils/SecurityAdminUtils.java @@ -1524,6 +1524,7 @@ public List> getAllProjectSettings(List projectFilte qs.addSelector(new QueryColumnSelector(projectPrefix + "COST", "project_cost")); qs.addSelector(new QueryColumnSelector(projectPrefix + "GLOBAL", "project_global")); qs.addSelector(new QueryColumnSelector(projectPrefix + "DISCOVERABLE", "project_discoverable")); + qs.addSelector(new QueryColumnSelector(projectPrefix + "IS_TEMPLATE", "project_is_template")); qs.addSelector(new QueryColumnSelector(projectPrefix + "CATALOGNAME", "project_catalog_name")); qs.addSelector(new QueryColumnSelector(projectPrefix + "CREATEDBY", "project_created_by")); qs.addSelector(new QueryColumnSelector(projectPrefix + "CREATEDBYTYPE", "project_created_by_type")); @@ -1689,6 +1690,38 @@ public boolean setProjectGlobal(String projectId, boolean global) { return true; } + /** + * Set whether viewers may clone a project as a template. + * + * @param projectId project identifier + * @param isTemplate whether the project is a template + * @return {@code true} when the flag is updated + */ + public boolean setProjectTemplate(String projectId, boolean isTemplate) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("UPDATE PROJECT SET IS_TEMPLATE=? WHERE PROJECTID=?"); + ps.setBoolean(1, isTemplate); + ps.setString(2, projectId); + int updatedRows = ps.executeUpdate(); + if (updatedRows != 1) { + throw new IllegalArgumentException("Project does not exist"); + } + if (!ps.getConnection().getAutoCommit()) { + ps.getConnection().commit(); + } + } catch (IllegalArgumentException e) { + throw e; + } catch (Exception e) { + classLogger.error("Failed to update project template setting", e); + throw new IllegalArgumentException("An error occurred setting the project template flag", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + return true; + } + /** * Set if the project is discoverable to all users on this instance * diff --git a/src/prerna/auth/utils/SecurityOwlCreator.java b/src/prerna/auth/utils/SecurityOwlCreator.java index e62b3639b2..6932291ef6 100644 --- a/src/prerna/auth/utils/SecurityOwlCreator.java +++ b/src/prerna/auth/utils/SecurityOwlCreator.java @@ -46,8 +46,7 @@ public class SecurityOwlCreator extends AbstractOwlCreator { static { relationshipsRequired.add( new String[] { "GITHUB_APP", "GITHUB_PROJECT_LINK", "GITHUB_APP.APP_ID.GITHUB_PROJECT_LINK.APP_ID" }); - relationshipsRequired.add( - new String[] { "ENGINE", "MODELMETADATA", "ENGINE.ENGINEID.MODELMETADATA.ENGINEID" }); + relationshipsRequired.add(new String[] { "ENGINE", "MODELMETADATA", "ENGINE.ENGINEID.MODELMETADATA.ENGINEID" }); } public SecurityOwlCreator(AbstractSqlQueryUtil queryUtil) { @@ -131,6 +130,7 @@ public void createColumnsAndTypes(AbstractSqlQueryUtil queryUtil) { Pair.with("PROJECTDISPLAYNAME", VARCHAR_255), Pair.with("GLOBAL", BOOLEAN_DATATYPE_NAME), Pair.with("DISCOVERABLE", BOOLEAN_DATATYPE_NAME), + Pair.with("IS_TEMPLATE", BOOLEAN_DATATYPE_NAME), Pair.with("TYPE", VARCHAR_255), Pair.with("COST", VARCHAR_255), Pair.with("CATALOGNAME", VARCHAR_255), diff --git a/src/prerna/auth/utils/SecurityProjectUtils.java b/src/prerna/auth/utils/SecurityProjectUtils.java index e1ef865abd..c9c24ae972 100644 --- a/src/prerna/auth/utils/SecurityProjectUtils.java +++ b/src/prerna/auth/utils/SecurityProjectUtils.java @@ -465,8 +465,8 @@ public static void addProject(String projectId, String projectName, String proje public static void addProject(String projectId, String projectName, String projectDisplayName, String projectType, String projectCost, boolean global, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String query = "INSERT INTO PROJECT (PROJECTID, PROJECTNAME, TYPE, COST, GLOBAL, DISCOVERABLE, CREATEDBY, CREATEDBYTYPE, DATECREATED, DATELASTEDITED, PROJECTDISPLAYNAME) " - + "VALUES (?,?,?,?,?,?,?,?,?,?,?)"; + String query = "INSERT INTO PROJECT (PROJECTID, PROJECTNAME, TYPE, COST, GLOBAL, DISCOVERABLE, IS_TEMPLATE, CREATEDBY, CREATEDBYTYPE, DATECREATED, DATELASTEDITED, PROJECTDISPLAYNAME) " + + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"; PreparedStatement ps = null; try { @@ -478,6 +478,7 @@ public static void addProject(String projectId, String projectName, String proje ps.setString(parameterIndex++, projectCost); ps.setBoolean(parameterIndex++, global); ps.setBoolean(parameterIndex++, false); + ps.setBoolean(parameterIndex++, false); if (user != null) { AuthProvider ap = user.getPrimaryLogin(); AccessToken token = user.getAccessToken(ap); @@ -1136,6 +1137,39 @@ public static boolean projectIsGlobal(String projectId) { return false; } + /** + * Determine whether a project has been explicitly enabled as a template. + * + * @param projectId project identifier + * @return {@code true} only when the persisted template flag is true + */ + public static boolean projectIsTemplate(String projectId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PROJECT__PROJECTID")); + qs.addExplicitFilter( + SimpleQueryFilter.makeColToValFilter("PROJECT__IS_TEMPLATE", "==", true, PixelDataType.BOOLEAN)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECT__PROJECTID", "==", projectId)); + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + return wrapper.hasNext(); + } catch (Exception e) { + classLogger.error("Failed to determine whether project is enabled as a template", e); + return false; + } + } + + /** + * Determine whether a user may clone a project. The user must be able to view + * the project and the owner must have explicitly enabled it as a template. + * + * @param user current user + * @param projectId project identifier + * @return whether the user may clone the project + */ + public static boolean userCanCloneProject(User user, String projectId) { + return userCanViewProject(user, projectId) && projectIsTemplate(projectId); + } + /** * * @param projectId @@ -1784,6 +1818,46 @@ public static boolean setProjectGlobal(User user, String projectId, boolean glob return true; } + /** + * Set whether viewers may clone a project as a template. + * + * @param user current user + * @param projectId project identifier + * @param isTemplate whether the project is a template + * @return {@code true} when the flag is updated + * @throws IllegalAccessException when the user is not the project owner + */ + public static boolean setProjectTemplate(User user, String projectId, boolean isTemplate) + throws IllegalAccessException { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + if (!SecurityUserProjectUtils.userIsOwner(user, projectId)) { + throw new IllegalAccessException( + "The user doesn't have permission to set this project as a template. Only the owner or an admin can perform this action."); + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("UPDATE PROJECT SET IS_TEMPLATE=? WHERE PROJECTID=?"); + ps.setBoolean(1, isTemplate); + ps.setString(2, projectId); + int updatedRows = ps.executeUpdate(); + if (updatedRows != 1) { + throw new IllegalArgumentException("Project does not exist"); + } + if (!ps.getConnection().getAutoCommit()) { + ps.getConnection().commit(); + } + } catch (IllegalArgumentException e) { + throw e; + } catch (Exception e) { + classLogger.error("Failed to update project template setting", e); + throw new IllegalArgumentException("An error occurred setting the project template flag", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + return true; + } + /** * Set a project and all its insights to be global * @@ -3184,6 +3258,14 @@ public static List> getUserProjectList(User user, List projectIdFilters, boolean favoritesOnly, Map projectMetadataFilter, List permissionFilters, String searchTerm, String limit, String offset, Map sortFields) { + return getUserProjectList(user, projectTypes, projectIdFilters, favoritesOnly, projectMetadataFilter, + permissionFilters, searchTerm, limit, offset, sortFields, false); + } + + public static List> getUserProjectList(User user, List projectTypes, + List projectIdFilters, boolean favoritesOnly, Map projectMetadataFilter, + List permissionFilters, String searchTerm, String limit, String offset, + Map sortFields, boolean onlyTemplates) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); boolean hasSearchTerm = searchTerm != null && !(searchTerm = searchTerm.trim()).isEmpty(); @@ -3201,6 +3283,7 @@ public static List> getUserProjectList(User user, List> getUserProjectList(User user, List> getUserProjectList(User user, String pro qs.addSelector(new QueryColumnSelector("PROJECT__COST", "project_cost")); qs.addSelector(new QueryColumnSelector("PROJECT__GLOBAL", "project_global")); qs.addSelector(new QueryColumnSelector("PROJECT__DISCOVERABLE", "project_discoverable")); + qs.addSelector(new QueryColumnSelector("PROJECT__IS_TEMPLATE", "project_is_template")); qs.addSelector(new QueryColumnSelector("PROJECT__CATALOGNAME", "project_catalog_name")); qs.addSelector(new QueryColumnSelector("PROJECT__CREATEDBY", "project_created_by")); qs.addSelector(new QueryColumnSelector("PROJECT__CREATEDBYTYPE", "project_created_by_type")); @@ -3709,6 +3798,7 @@ public static List> getDiscoverableProjectList(String projec qs.addSelector(new QueryColumnSelector("PROJECT__COST", "project_cost")); qs.addSelector(new QueryColumnSelector("PROJECT__GLOBAL", "project_global")); qs.addSelector(new QueryColumnSelector("PROJECT__DISCOVERABLE", "project_discoverable")); + qs.addSelector(new QueryColumnSelector("PROJECT__IS_TEMPLATE", "project_is_template")); qs.addSelector(new QueryColumnSelector("PROJECT__CATALOGNAME", "project_catalog_name")); qs.addSelector(new QueryColumnSelector("PROJECT__CREATEDBY", "project_created_by")); qs.addSelector(new QueryColumnSelector("PROJECT__CREATEDBYTYPE", "project_created_by_type")); diff --git a/src/prerna/auth/utils/reactors/admin/AdminProjectInfoReactor.java b/src/prerna/auth/utils/reactors/admin/AdminProjectInfoReactor.java index ca61b55109..30a2d1660d 100644 --- a/src/prerna/auth/utils/reactors/admin/AdminProjectInfoReactor.java +++ b/src/prerna/auth/utils/reactors/admin/AdminProjectInfoReactor.java @@ -138,7 +138,7 @@ public String getReactorDescription() { Returns a single map (PROJECT_INFO/CUSTOM_DATA_STRUCTURE) containing: Core project fields: project_id, project_name, project_display_name, project_type, project_cost, - project_global, project_discoverable, project_catalog_name, project_created_by, project_created_by_type, + project_global, project_discoverable, project_is_template, project_catalog_name, project_created_by, project_created_by_type, project_date_created, project_date_last_edited, low_project_name. Portal fields: project_has_portal, project_portal_name, project_portal_published_date, project_published_user, project_published_user_type, project_reactors_compiled_date, project_reactors_compiled_user, @@ -158,4 +158,4 @@ protected String getDescriptionForKey(String key) { return super.getDescriptionForKey(key); } -} \ No newline at end of file +} diff --git a/src/prerna/reactor/agent/skill/CloneSkillReactor.java b/src/prerna/reactor/agent/skill/CloneSkillReactor.java index 359656a908..ae9ae16fd4 100644 --- a/src/prerna/reactor/agent/skill/CloneSkillReactor.java +++ b/src/prerna/reactor/agent/skill/CloneSkillReactor.java @@ -61,7 +61,8 @@ *
    *
  • Every file under the source's skill content folder (SKILL.md + any * helpers).
  • - *
  • The description, re-synthesized into the clone's SKILL.md frontmatter.
  • + *
  • The description, re-synthesized into the clone's SKILL.md + * frontmatter.
  • *
* *

@@ -82,8 +83,8 @@ * * *

- * Authorization: caller must be able to view the source skill-project - * ({@link SecurityProjectUtils#userCanViewProject}). + * Authorization: caller must be able to view the source skill-project and the + * project must be explicitly enabled as a template. */ public class CloneSkillReactor extends AbstractReactor { @@ -112,6 +113,9 @@ public NounMetadata execute() { throw new IllegalArgumentException( "Skill " + sourceSkillId + " does not exist or user does not have permission to view it"); } + if (!SecurityProjectUtils.userCanCloneProject(user, sourceSkillId)) { + throw new IllegalArgumentException("This skill is not enabled as a template and cannot be cloned."); + } if (!SkillProjects.isSkillProject(sourceSkillId)) { throw new IllegalArgumentException("Skill not found: " + sourceSkillId); diff --git a/src/prerna/reactor/project/CreateAppFromTemplateReactor.java b/src/prerna/reactor/project/CreateAppFromTemplateReactor.java index 6c8bd99c38..990f7485e7 100644 --- a/src/prerna/reactor/project/CreateAppFromTemplateReactor.java +++ b/src/prerna/reactor/project/CreateAppFromTemplateReactor.java @@ -47,8 +47,8 @@ import prerna.auth.User; import prerna.auth.utils.SecurityProjectUtils; -import prerna.engine.impl.model.inferencetracking.ModelInferenceLogsUtils; import prerna.cluster.util.ClusterUtil; +import prerna.engine.impl.model.inferencetracking.ModelInferenceLogsUtils; import prerna.project.api.IProject; import prerna.project.impl.ProjectHelper; import prerna.reactor.AbstractReactor; @@ -66,7 +66,8 @@ public class CreateAppFromTemplateReactor extends AbstractReactor { private static final String CLASS_NAME = CreateAppFromTemplateReactor.class.getName(); - // PROJECTMETA tag marking a project as platform-managed; never copied onto a clone + // PROJECTMETA tag marking a project as platform-managed; + // never copied onto a clone private static final String SYSTEM_TAG = "SYSTEM"; /* @@ -80,11 +81,6 @@ public CreateAppFromTemplateReactor() { ReactorKeysEnum.GLOBAL.getKey(), ReactorKeysEnum.PROVIDER.getKey(), ReactorKeysEnum.URL.getKey() }; } - // Allow viewer cloning for agents and skills but not apps - static boolean canCloneProject(IProject.PROJECT_TYPE projectType, boolean canEdit) { - return projectType == IProject.PROJECT_TYPE.WORKSPACE || projectType == IProject.PROJECT_TYPE.SKILL || canEdit; - } - @Override public NounMetadata execute() { Logger logger = getLogger(CLASS_NAME); @@ -108,10 +104,8 @@ public NounMetadata execute() { // Use the template to populate the parameters needed to create the new project IProject.PROJECT_TYPE projectEnumType = templateProject.getProjectType(); - boolean canEdit = SecurityProjectUtils.userCanEditProject(this.insight.getUser(), projectTemplateId); - if (!canCloneProject(projectEnumType, canEdit)) { - throw new IllegalArgumentException( - "Only owners and editors can clone regular projects. Agents and skills can be cloned with read-only access."); + if (!SecurityProjectUtils.userCanCloneProject(this.insight.getUser(), projectTemplateId)) { + throw new IllegalArgumentException("This project is not enabled as a template and cannot be cloned."); } // Create new project @@ -163,7 +157,6 @@ public NounMetadata execute() { + e.getMessage()); } - if (IProject.PROJECT_TYPE.WORKSPACE == projectEnumType) { try { User user = this.insight.getUser(); @@ -198,8 +191,7 @@ public NounMetadata execute() { } SecurityProjectUtils.updateProjectDependencies(user, newProjectId, dependencyList); - ModelInferenceLogsUtils.createNewWorkspaceEntry(newProjectId, - user.getPrimaryLoginToken().getId(), + ModelInferenceLogsUtils.createNewWorkspaceEntry(newProjectId, user.getPrimaryLoginToken().getId(), newProjectName, sourceDescription, sourceSystemPrompt, clonedResources); if (sourceConfigJson != null) { ModelInferenceLogsUtils.updateWorkspaceConfigJson(newProjectId, sourceConfigJson); @@ -226,7 +218,8 @@ public NounMetadata execute() { SecurityProjectUtils.updateProjectMetadata(newProjectId, tagUpdate); } } catch (Exception e) { - classLogger.error("Failed to clone workspace inference log entry from template '{}' to new project '{}'.", + classLogger.error( + "Failed to clone workspace inference log entry from template '{}' to new project '{}'.", projectTemplateId, newProject.getProjectId(), e); try { newProject.delete(); diff --git a/src/prerna/reactor/project/MyProjectsReactor.java b/src/prerna/reactor/project/MyProjectsReactor.java index a596bfc96a..43ac99e78c 100644 --- a/src/prerna/reactor/project/MyProjectsReactor.java +++ b/src/prerna/reactor/project/MyProjectsReactor.java @@ -56,7 +56,7 @@ public MyProjectsReactor() { ReactorKeysEnum.META_KEYS.getKey(), ReactorKeysEnum.META_FILTERS.getKey(), ReactorKeysEnum.PERMISSION_FILTERS.getKey(), ReactorKeysEnum.NO_META.getKey(), ReactorKeysEnum.PROJECT_TYPE.getKey(), ReactorKeysEnum.INCLUDE_USERTRACKING_KEY.getKey(), - ReactorKeysEnum.SORT.getKey() }; + ReactorKeysEnum.SORT.getKey(), ReactorKeysEnum.ONLY_TEMPLATES.getKey() }; } @Override @@ -70,6 +70,7 @@ public NounMetadata execute() { boolean noMeta = getBoolean(ReactorKeysEnum.NO_META.getKey(), false); List permissionFilters = getListInteger(ReactorKeysEnum.PERMISSION_FILTERS.getKey()); boolean includeUserT = getBoolean(ReactorKeysEnum.INCLUDE_USERTRACKING_KEY.getKey(), false); + boolean onlyTemplates = getBoolean(ReactorKeysEnum.ONLY_TEMPLATES.getKey(), false); Map projectMetadataFilter = getMap(ReactorKeysEnum.META_FILTERS.getKey()); Map sortFields = getMap(ReactorKeysEnum.SORT.getKey()); @@ -77,7 +78,7 @@ public NounMetadata execute() { // in some smss files List> projectInfo = SecurityProjectUtils.getUserProjectList(this.insight.getUser(), projectTypeFilters, projectIdFilters, favoritesOnly, projectMetadataFilter, permissionFilters, - searchTerm, limit, offset, sortFields); + searchTerm, limit, offset, sortFields, onlyTemplates); if (!projectInfo.isEmpty() && (!noMeta || includeUserT)) { Map index = new HashMap<>(projectInfo.size()); @@ -233,6 +234,9 @@ public JSONObject getResponseSchema() { itemProperties.put("project_global", new JSONObject().put("type", "boolean").put("description", "Whether the project is globally accessible")); + itemProperties.put("project_is_template", new JSONObject().put("type", "boolean").put("description", + "Whether viewers may clone the project as a template")); + itemProperties.put("project_favorite", new JSONObject().put("type", "integer").put("enum", new JSONArray().put(0).put(1)).put("description", "1 if user has favorited this project, 0 otherwise")); diff --git a/src/prerna/reactor/project/ProjectInfoReactor.java b/src/prerna/reactor/project/ProjectInfoReactor.java index caa5a179d3..d27ad31a3e 100644 --- a/src/prerna/reactor/project/ProjectInfoReactor.java +++ b/src/prerna/reactor/project/ProjectInfoReactor.java @@ -145,7 +145,7 @@ public String getReactorDescription() { Returns a single map (PROJECT_INFO/CUSTOM_DATA_STRUCTURE) containing: Core project fields: project_id, project_name, project_display_name, project_type, project_cost, - project_global, project_discoverable, project_catalog_name, project_created_by, project_created_by_type, + project_global, project_discoverable, project_is_template, project_catalog_name, project_created_by, project_created_by_type, project_date_created, project_date_last_edited, low_project_name, project_description. Portal fields: project_has_portal, project_portal_name, project_portal_published_date, project_published_user, project_published_user_type, project_reactors_compiled_date, project_reactors_compiled_user, @@ -170,4 +170,4 @@ protected String getDescriptionForKey(String key) { return super.getDescriptionForKey(key); } -} \ No newline at end of file +} diff --git a/src/prerna/sablecc2/om/ReactorKeysEnum.java b/src/prerna/sablecc2/om/ReactorKeysEnum.java index 2bf4654aa3..6c944c70d5 100644 --- a/src/prerna/sablecc2/om/ReactorKeysEnum.java +++ b/src/prerna/sablecc2/om/ReactorKeysEnum.java @@ -31,7 +31,7 @@ public enum ReactorKeysEnum { - // @formatter:off + // @formatter:off ACCESS("access", "Boolean to check if access is required."), ADDITIONAL_DATA_TYPE("additionalDataType", "Additional data types defining specific format"), ADDITIONAL_DATA_TYPES("additionalDataTypes", "Additional data types defining specific formats"), @@ -206,7 +206,8 @@ public enum ReactorKeysEnum { NUMERIC_VALUE("numValue", "Numeric value to be used in the operation"), NUMERIC_VALUES("numValues", "Numeric values to be used in the operation"), OFFSET("offset", "Offset to add for the query results"), - ONLY_FAVORITES("onlyFavorites", "Get engines/insights which are favorited by the user"), + ONLY_FAVORITES("onlyFavorites", "Get engines/insights which are favorited by the user"), + ONLY_TEMPLATES("onlyTemplates", "Get projects which are enabled as templates"), OPERATOR("operator", "The operator to use for identifying this filter such as > , < = != etc. "), OPERATORU("operatoru", "Unique operator to use for identifying this filter such as > , < = != etc. For instance this can be a = 1 AND a = 2 in which case the first = would be and.left.="), OPTIONS("options", "Map of option values"),