AMBARI-19426. Modify quick link resource provider to consider filters and return visibility. (Balazs Bence Sari via stoader)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/2643dd65 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/2643dd65 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/2643dd65 Branch: refs/heads/trunk Commit: 2643dd65348272f69b91160990d6e484289572ba Parents: 6483710 Author: Balazs Bence Sari <bs...@hortonworks.com> Authored: Wed Jan 11 21:12:46 2017 +0100 Committer: Toader, Sebastian <stoa...@hortonworks.com> Committed: Wed Jan 11 21:13:14 2017 +0100 ---------------------------------------------------------------------- .../controller/AmbariManagementController.java | 7 + .../AmbariManagementControllerImpl.java | 20 ++ .../QuickLinkArtifactResourceProvider.java | 22 ++ .../ambari/server/state/quicklinks/Link.java | 11 + .../DefaultQuickLinkVisibilityController.java | 213 ++++++++++++++++++ .../QuickLinkVisibilityController.java | 37 ++++ .../QuickLinkVisibilityControllerFactory.java | 57 +++++ .../quicklinksprofile/QuickLinksProfile.java | 10 +- .../QuickLinksProfileEvaluationException.java | 31 +++ .../QuickLinksProfileEvaluator.java | 202 ----------------- .../QuickLinksProfileEvaluatorException.java | 27 --- .../ShowAllLinksVisibilityController.java | 38 ++++ .../QuickLinkArtifactResourceProviderTest.java | 198 +++++++++++++++++ .../state/quicklinksprofile/EvaluatorTest.java | 203 ----------------- .../quicklinksprofile/FilterEvaluatorTest.java | 218 +++++++++++++++++++ ...uickLinkVisibilityControllerFactoryTest.java | 82 +++++++ .../QuickLinkVisibilityControllerTest.java | 181 +++++++++++++++ .../QuickLinksProfileEvaluatorTest.java | 168 -------------- .../QuickLinksProfileParserTest.java | 8 +- .../resources/example_quicklinks_profile.json | 9 + .../inconsistent_quicklinks_profile_2.json | 13 ++ 21 files changed, 1150 insertions(+), 605 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java index cc20324..91bfe09 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java @@ -59,6 +59,7 @@ import org.apache.ambari.server.state.ServiceInfo; import org.apache.ambari.server.state.ServiceOsSpecific; import org.apache.ambari.server.state.State; import org.apache.ambari.server.state.configgroup.ConfigGroupFactory; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController; import org.apache.ambari.server.state.scheduler.RequestExecutionFactory; /** @@ -912,5 +913,11 @@ public interface AmbariManagementController { */ MetricsCollectorHAManager getMetricsCollectorHAManager(); + /** + * @return the visibility controller that decides which quicklinks should be visible + * based on the actual quick links profile. If no profile is set, all links will be shown. + */ + QuickLinkVisibilityController getQuicklinkVisibilityController(); + } http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index 3963a19..5e8c803 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -112,6 +112,7 @@ import org.apache.ambari.server.orm.dao.ClusterVersionDAO; import org.apache.ambari.server.orm.dao.ExtensionDAO; import org.apache.ambari.server.orm.dao.ExtensionLinkDAO; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; +import org.apache.ambari.server.orm.dao.SettingDAO; import org.apache.ambari.server.orm.dao.StackDAO; import org.apache.ambari.server.orm.dao.WidgetDAO; import org.apache.ambari.server.orm.dao.WidgetLayoutDAO; @@ -122,6 +123,7 @@ import org.apache.ambari.server.orm.entities.ExtensionLinkEntity; import org.apache.ambari.server.orm.entities.OperatingSystemEntity; import org.apache.ambari.server.orm.entities.RepositoryEntity; import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; +import org.apache.ambari.server.orm.entities.SettingEntity; import org.apache.ambari.server.orm.entities.StackEntity; import org.apache.ambari.server.orm.entities.WidgetEntity; import org.apache.ambari.server.orm.entities.WidgetLayoutEntity; @@ -181,6 +183,8 @@ import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.StackInfo; import org.apache.ambari.server.state.State; import org.apache.ambari.server.state.configgroup.ConfigGroupFactory; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory; import org.apache.ambari.server.state.repository.VersionDefinitionXml; import org.apache.ambari.server.state.scheduler.RequestExecutionFactory; import org.apache.ambari.server.state.stack.RepositoryXml; @@ -235,6 +239,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle public static final String SKIP_INSTALL_FOR_COMPONENTS = "skipInstallForComponents"; public static final String DONT_SKIP_INSTALL_FOR_COMPONENTS = "dontSkipInstallForComponents"; + /** + * The name of the ambari setting that stores the quicklinks profile. + * See {@link org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile} + */ + public static final String SETTING_QUICKLINKS_PROFILE = "QuickLinksProfile"; + private final Clusters clusters; private final ActionManager actionManager; @@ -299,6 +309,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle private AmbariEventPublisher ambariEventPublisher; @Inject private MetricsCollectorHAManager metricsCollectorHAManager; + @Inject + private SettingDAO settingDAO; private MaintenanceStateHelper maintenanceStateHelper; @@ -5512,4 +5524,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle linkEntity.setExtension(extension); return linkEntity; } + + @Override + public QuickLinkVisibilityController getQuicklinkVisibilityController() { + SettingEntity entity = settingDAO.findByName(SETTING_QUICKLINKS_PROFILE); + String quickLinkProfileJson = null != entity ? entity.getContent() : null; + return QuickLinkVisibilityControllerFactory.get(quickLinkProfileJson); + } + } http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java index 5603765..714ae53 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java @@ -41,6 +41,9 @@ import org.apache.ambari.server.controller.utilities.PropertyHelper; import org.apache.ambari.server.state.QuickLinksConfigurationInfo; import org.apache.ambari.server.state.ServiceInfo; import org.apache.ambari.server.state.StackInfo; +import org.apache.ambari.server.state.quicklinks.Link; +import org.apache.ambari.server.state.quicklinks.QuickLinks; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController; public class QuickLinkArtifactResourceProvider extends AbstractControllerResourceProvider { @@ -174,6 +177,8 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc } } + setVisibility(serviceInfo.getName(), serviceQuickLinks); + List<Resource> serviceResources = new ArrayList<Resource>(); for (QuickLinksConfigurationInfo quickLinksConfigurationInfo : serviceQuickLinks) { Resource resource = new ResourceImpl(Resource.Type.QuickLink); @@ -194,6 +199,23 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc return resources; } + /** + * Sets the visibility flag of the links based on the actual quicklinks profile (if exists). + * @param serviceName the name of the service + * @param serviceQuickLinks the links + */ + private void setVisibility(String serviceName, List<QuickLinksConfigurationInfo> serviceQuickLinks) { + QuickLinkVisibilityController visibilityController = getManagementController().getQuicklinkVisibilityController(); + + for(QuickLinksConfigurationInfo configurationInfo: serviceQuickLinks) { + for (QuickLinks links: configurationInfo.getQuickLinksConfigurationMap().values()) { + for(Link link: links.getQuickLinksConfiguration().getLinks()) { + link.setVisible(visibilityController.isVisible(serviceName, link)); + } + } + } + } + @Override protected Set<String> getPKPropertyIds() { return null; http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java index f589f5d..1d2e712 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java @@ -53,6 +53,9 @@ public class Link{ @JsonProperty("attributes") private List<String> attributes; + @JsonProperty("visible") + private boolean visible = true; + public String getName() { return name; } @@ -109,6 +112,14 @@ public class Link{ this.protocol = protocol; } + public boolean isVisible() { + return visible; + } + + public void setVisible(boolean visible) { + this.visible = visible; + } + @Nullable public List<String> getAttributes() { return attributes; http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java new file mode 100644 index 0000000..730f01b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.apache.ambari.server.state.quicklinks.Link; + +import com.google.common.base.Optional; + +/** + * This class can evaluate whether a quicklink has to be shown or hidden based on the received {@link QuickLinksProfile}. + */ +public class DefaultQuickLinkVisibilityController implements QuickLinkVisibilityController { + private final FilterEvaluator globalRules; + private final Map<String, FilterEvaluator> serviceRules = new HashMap<>(); + private final Map<ServiceComponent, FilterEvaluator> componentRules = new HashMap<>(); + + public DefaultQuickLinkVisibilityController(QuickLinksProfile profile) throws QuickLinksProfileEvaluationException { + int filterCount = size(profile.getFilters()); + globalRules = new FilterEvaluator(profile.getFilters()); + for (Service service: nullToEmptyList(profile.getServices())) { + filterCount += size(service.getFilters()); + serviceRules.put(service.getName(), new FilterEvaluator(service.getFilters())); + for (Component component: nullToEmptyList(service.getComponents())) { + filterCount += size(component.getFilters()); + componentRules.put(ServiceComponent.of(service.getName(), component.getName()), + new FilterEvaluator(component.getFilters())); + } + } + if (filterCount == 0) { + throw new QuickLinksProfileEvaluationException("At least one filter must be defined."); + } + } + + /** + * @param service the name of the service + * @param quickLink the quicklink + * @return a boolean indicating whether the link in the parameter should be visible + */ + public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) { + // First, component rules are evaluated if exist and applicable + Optional<Boolean> componentResult = evaluateComponentRules(service, quickLink); + if (componentResult.isPresent()) { + return componentResult.get(); + } + + // Secondly, service level rules are applied + Optional<Boolean> serviceResult = evaluateServiceRules(service, quickLink); + if (serviceResult.isPresent()) { + return serviceResult.get(); + } + + // Global rules are evaluated lastly. If no rules apply to the link, it will be hidden. + return globalRules.isVisible(quickLink).or(false); + } + + private int size(@Nullable Collection<?> collection) { + return null == collection ? 0 : collection.size(); + } + + private Optional<Boolean> evaluateComponentRules(@Nonnull String service, @Nonnull Link quickLink) { + if (null == quickLink.getComponentName()) { + return Optional.absent(); + } + else { + FilterEvaluator componentEvaluator = componentRules.get(ServiceComponent.of(service, quickLink.getComponentName())); + return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.<Boolean>absent(); + } + } + + private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) { + return serviceRules.containsKey(service) ? + serviceRules.get(service).isVisible(quickLink) : Optional.<Boolean>absent(); + } + + static <T> List<T> nullToEmptyList(@Nullable List<T> items) { + return items != null ? items : Collections.<T>emptyList(); + } +} + +/** + * Groups quicklink filters that are on the same level (e.g. a global evaluator or an evaluator for the "HDFS" service, + * etc.). The evaluator pick the most applicable filter for a given quick link. If no applicable filter is found, it + * returns {@link Optional#absent()}. + * <p> + * Filter evaluation order is the following: + * <ol> + * <li>First, link name filters are evaluated. These match links by name.</li> + * <li>If there is no matching link name filter, link attribute filters are evaluated next. "Hide" type filters + * take precedence to "show" type filters.</li> + * <li>Finally, the match-all filter is evaluated, provided it exists.</li> + * </ol> + * </p> + */ +class FilterEvaluator { + private final Map<String, Boolean> linkNameFilters = new HashMap<>(); + private final Set<String> showAttributes = new HashSet<>(); + private final Set<String> hideAttributes = new HashSet<>(); + private Optional<Boolean> acceptAllFilter = Optional.absent(); + + FilterEvaluator(List<Filter> filters) throws QuickLinksProfileEvaluationException { + for (Filter filter: DefaultQuickLinkVisibilityController.nullToEmptyList(filters)) { + if (filter instanceof LinkNameFilter) { + String linkName = ((LinkNameFilter)filter).getLinkName(); + if (linkNameFilters.containsKey(linkName) && linkNameFilters.get(linkName) != filter.isVisible()) { + throw new QuickLinksProfileEvaluationException("Contradicting filters for link name [" + linkName + "]"); + } + linkNameFilters.put(linkName, filter.isVisible()); + } + else if (filter instanceof LinkAttributeFilter) { + String linkAttribute = ((LinkAttributeFilter)filter).getLinkAttribute(); + if (filter.isVisible()) { + showAttributes.add(linkAttribute); + } + else { + hideAttributes.add(linkAttribute); + } + if (showAttributes.contains(linkAttribute) && hideAttributes.contains(linkAttribute)) { + throw new QuickLinksProfileEvaluationException("Contradicting filters for link attribute [" + linkAttribute + "]"); + } + } + // If none of the above, it is an accept-all filter. We expect only one of this type for an Evaluator + else { + if (acceptAllFilter.isPresent() && !acceptAllFilter.get().equals(filter.isVisible())) { + throw new QuickLinksProfileEvaluationException("Contradicting accept-all filters."); + } + acceptAllFilter = Optional.of(filter.isVisible()); + } + } + } + + /** + * @param quickLink the link to evaluate + * @return Three way evaluation result, which can be one of these: + * show: Optional.of(true), hide: Optional.of(false), don't know: absent optional + */ + Optional<Boolean> isVisible(Link quickLink) { + // process first priority filters based on link name + if (linkNameFilters.containsKey(quickLink.getName())) { + return Optional.of(linkNameFilters.get(quickLink.getName())); + } + + // process second priority filters based on link attributes + // 'hide' rules take precedence over 'show' rules + for (String attribute: DefaultQuickLinkVisibilityController.nullToEmptyList(quickLink.getAttributes())) { + if (hideAttributes.contains(attribute)) return Optional.of(false); + } + for (String attribute: DefaultQuickLinkVisibilityController.nullToEmptyList(quickLink.getAttributes())) { + if (showAttributes.contains(attribute)) return Optional.of(true); + } + + // accept all filter (if exists) is the last priority + return acceptAllFilter; + } +} + +/** + * Simple value class encapsulating a link name an component name. + */ +class ServiceComponent { + private final String service; + private final String component; + + ServiceComponent(String service, String component) { + this.service = service; + this.component = component; + } + + static ServiceComponent of(String service, String component) { + return new ServiceComponent(service, component); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceComponent that = (ServiceComponent) o; + return Objects.equals(service, that.service) && + Objects.equals(component, that.component); + } + + @Override + public int hashCode() { + return Objects.hash(service, component); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java new file mode 100644 index 0000000..caa2e2e --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + + +import javax.annotation.Nonnull; + +import org.apache.ambari.server.state.quicklinks.Link; + + +public interface QuickLinkVisibilityController { + + /** + * @param service The name of the service the quicklink belongs to + * @param quickLink the link + * @return a boolean indicating if the link should be visible + */ + boolean isVisible(@Nonnull String service, @Nonnull Link quickLink); + +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java new file mode 100644 index 0000000..5cafb48 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactory.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +import java.io.IOException; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This factory returns the quicklinks visibility controller. If the received string is a parseable and valid quicklinks + * profile, a {@link DefaultQuickLinkVisibilityController} will be returned based on the profile. If the string is + * {@code null} or invalid {@link ShowAllLinksVisibilityController} will be returned that displays all links which + * corresponds to the legacy behavior. + */ +public class QuickLinkVisibilityControllerFactory { + private static final Logger LOG = LoggerFactory.getLogger(QuickLinkVisibilityControllerFactory.class); + + /** + * @param quickLinkProfileJson the quick links profile or {@code null} if no profile is set + * @return a {@link DefaultQuickLinkVisibilityController} if the profile exists and is valid, {@link ShowAllLinksVisibilityController} + * otherwise. + */ + public static QuickLinkVisibilityController get(@Nullable String quickLinkProfileJson) { + if (null == quickLinkProfileJson) { + LOG.info("No quick link profile is set, will display all quicklinks."); + return new ShowAllLinksVisibilityController(); + } + try { + QuickLinksProfile profile = new QuickLinksProfileParser().parse(quickLinkProfileJson.getBytes()); + return new DefaultQuickLinkVisibilityController(profile); + } + catch (IOException | QuickLinksProfileEvaluationException ex) { + LOG.error("Unable to parse quick link profile json: " + quickLinkProfileJson, ex); + return new ShowAllLinksVisibilityController(); + } + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java index e86af38..c9ac6b4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java @@ -25,15 +25,17 @@ import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; /** - * A quicklinks profile is essentially a set of filters defined on three levels: + * A quicklinks profile is essentially a set of quick link filters defined on three levels: * <ul> * <li>global level</li> * <li>service level</li> * <li>component level (within a service)</li> * </ul> - * - * For each link, filters are evaluated bottom up: component level filters take priority to service level filters - * and service level filters take priority to global filters. + * <p>For each link, filters are evaluated bottom up: component level filters take priority to service level filters + * and service level filters take priority to global filters.</p> + * <p>When a quick link profile is set in Ambari, then each quick link's visibility flag is updated according to the profile + * before being returned by {@link org.apache.ambari.server.controller.internal.QuickLinkArtifactResourceProvider}.</p> + * <p>When no profile is set, all quick link's visibility flat will be set to {@code true} by the provider</p> */ @JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java new file mode 100644 index 0000000..26819e1 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +/** + * Exception that is thrown if a {@link QuickLinkVisibilityController} cannot be build due to errors in the + * {@link QuickLinksProfile}. + */ +public class QuickLinksProfileEvaluationException extends Exception { + + public QuickLinksProfileEvaluationException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java deleted file mode 100644 index 31335b6..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluator.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ambari.server.state.quicklinksprofile; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.ambari.server.state.quicklinks.Link; - -import com.google.common.base.Optional; - -/** - * This class can evaluate whether a quicklink has to be shown or hidden based on the received {@link QuickLinksProfile}. - */ -public class QuickLinksProfileEvaluator { - private final Evaluator globalRules; - private final Map<String, Evaluator> serviceRules = new HashMap<>(); - private final Map<ServiceComponent, Evaluator> componentRules = new HashMap<>(); - - public QuickLinksProfileEvaluator(QuickLinksProfile profile) throws QuickLinksProfileEvaluatorException { - globalRules = new Evaluator(profile.getFilters()); - for (Service service: nullToEmptyList(profile.getServices())) { - serviceRules.put(service.getName(), new Evaluator(service.getFilters())); - for (Component component: nullToEmptyList(service.getComponents())) { - componentRules.put(ServiceComponent.of(service.getName(), component.getName()), - new Evaluator(component.getFilters())); - } - } - } - - /** - * @param service the name of the service - * @param quickLink the quicklink - * @return a boolean indicating whether the link in the parameter should be visible - */ - public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) { - // First, component rules are evaluated if exist and applicable - Optional<Boolean> componentResult = evaluateComponentRules(service, quickLink); - if (componentResult.isPresent()) { - return componentResult.get(); - } - - // Secondly, service level rules are applied - Optional<Boolean> serviceResult = evaluateServiceRules(service, quickLink); - if (serviceResult.isPresent()) { - return serviceResult.get(); - } - - // Global rules are evaluated lastly. If no rules apply to the link, it will be hidden. - return globalRules.isVisible(quickLink).or(false); - } - - private Optional<Boolean> evaluateComponentRules(@Nonnull String service, @Nonnull Link quickLink) { - if (null == quickLink.getComponentName()) { - return Optional.absent(); - } - else { - Evaluator componentEvaluator = componentRules.get(ServiceComponent.of(service, quickLink.getComponentName())); - return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.<Boolean>absent(); - } - } - - private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) { - return serviceRules.containsKey(service) ? - serviceRules.get(service).isVisible(quickLink) : Optional.<Boolean>absent(); - } - - static <T> List<T> nullToEmptyList(@Nullable List<T> items) { - return items != null ? items : Collections.<T>emptyList(); - } -} - -/** - * Groups quicklink filters that are on the same level (e.g. a global evaluator or an evaluator for the "HDFS" service, - * etc.). The evaluator pick the most applicable filter for a given quick link. If no applicable filter is found, it - * returns {@link Optional#absent()}. - * <p> - * Filter evaluation order is the following: - * <ol> - * <li>First, link name filters are evaluated. These match links by name.</li> - * <li>If there is no matching link name filter, link attribute filters are evaluated next. "Hide" type filters - * take precedence to "show" type filters.</li> - * <li>Finally, the match-all filter is evaluated, provided it exists.</li> - * </ol> - * </p> - */ -class Evaluator { - private final Map<String, Boolean> linkNameFilters = new HashMap<>(); - private final Set<String> showAttributes = new HashSet<>(); - private final Set<String> hideAttributes = new HashSet<>(); - private Optional<Boolean> acceptAllFilter = Optional.absent(); - - Evaluator(List<Filter> filters) throws QuickLinksProfileEvaluatorException { - for (Filter filter: QuickLinksProfileEvaluator.nullToEmptyList(filters)) { - if (filter instanceof LinkNameFilter) { - String linkName = ((LinkNameFilter)filter).getLinkName(); - if (linkNameFilters.containsKey(linkName) && linkNameFilters.get(linkName) != filter.isVisible()) { - throw new QuickLinksProfileEvaluatorException("Contradicting filters for link name [" + linkName + "]"); - } - linkNameFilters.put(linkName, filter.isVisible()); - } - else if (filter instanceof LinkAttributeFilter) { - String linkAttribute = ((LinkAttributeFilter)filter).getLinkAttribute(); - if (filter.isVisible()) { - showAttributes.add(linkAttribute); - } - else { - hideAttributes.add(linkAttribute); - } - if (showAttributes.contains(linkAttribute) && hideAttributes.contains(linkAttribute)) { - throw new QuickLinksProfileEvaluatorException("Contradicting filters for link attribute [" + linkAttribute + "]"); - } - } - // If none of the above, it is an accept-all filter. We expect only one for an Evaluator - else { - if (acceptAllFilter.isPresent() && !acceptAllFilter.get().equals(filter.isVisible())) { - throw new QuickLinksProfileEvaluatorException("Contradicting accept-all filters."); - } - acceptAllFilter = Optional.of(filter.isVisible()); - } - } - } - - /** - * @param quickLink the link to evaluate - * @return Three way evaluation result, which can be one of these: - * show: Optional.of(true), hide: Optional.of(false), don't know: absent optional - */ - Optional<Boolean> isVisible(Link quickLink) { - // process first priority filters based on link name - if (linkNameFilters.containsKey(quickLink.getName())) { - return Optional.of(linkNameFilters.get(quickLink.getName())); - } - - // process second priority filters based on link attributes - // 'hide' rules take precedence over 'show' rules - for (String attribute: QuickLinksProfileEvaluator.nullToEmptyList(quickLink.getAttributes())) { - if (hideAttributes.contains(attribute)) return Optional.of(false); - } - for (String attribute: QuickLinksProfileEvaluator.nullToEmptyList(quickLink.getAttributes())) { - if (showAttributes.contains(attribute)) return Optional.of(true); - } - - // accept all filter (if exists) is the last priority - return acceptAllFilter; - } -} - -/** - * Simple value class encapsulating a link name an component name. - */ -class ServiceComponent { - private final String service; - private final String component; - - ServiceComponent(String service, String component) { - this.service = service; - this.component = component; - } - - static ServiceComponent of(String service, String component) { - return new ServiceComponent(service, component); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ServiceComponent that = (ServiceComponent) o; - return Objects.equals(service, that.service) && - Objects.equals(component, that.component); - } - - @Override - public int hashCode() { - return Objects.hash(service, component); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java deleted file mode 100644 index c24281a..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ambari.server.state.quicklinksprofile; - -public class QuickLinksProfileEvaluatorException extends Exception { - - public QuickLinksProfileEvaluatorException(String message) { - super(message); - } - -} http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java new file mode 100644 index 0000000..286a10c --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/ShowAllLinksVisibilityController.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + + +import javax.annotation.Nonnull; + +import org.apache.ambari.server.state.quicklinks.Link; + +/** + * A visibility controller that shows all quicklinks. This is used when no quicklinks profile is set or the profile is + * invalid. This mimics legacy behavior when all quicklinks were shown. + * + */ +public class ShowAllLinksVisibilityController implements QuickLinkVisibilityController { + + @Override + public boolean isVisible(@Nonnull String service, @Nonnull Link quickLink) { + return true; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java new file mode 100644 index 0000000..8c723c9 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java @@ -0,0 +1,198 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import static org.easymock.EasyMock.anyString; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.api.services.AmbariMetaInfo; +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.Predicate; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.utilities.PredicateBuilder; +import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.stack.QuickLinksConfigurationModule; +import org.apache.ambari.server.state.QuickLinksConfigurationInfo; +import org.apache.ambari.server.state.ServiceInfo; +import org.apache.ambari.server.state.StackInfo; +import org.apache.ambari.server.state.quicklinks.Link; +import org.apache.ambari.server.state.quicklinks.QuickLinks; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController; +import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory; +import org.easymock.IAnswer; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.common.io.Resources; +import com.google.inject.Binder; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +public class QuickLinkArtifactResourceProviderTest { + + private Injector injector; + String quicklinkProfile; + + @Before + public void before() { + injector = Guice.createInjector(new MockModule()); + } + + private QuickLinkArtifactResourceProvider createProvider() { + return new QuickLinkArtifactResourceProvider(injector.getInstance(AmbariManagementController.class)); + } + + /** + * Test to prove the returned links' visibility reflect the actual profile + */ + @Test + public void getResourcesRespectsVisibility() throws Exception { + quicklinkProfile = Resources.toString(Resources.getResource("example_quicklinks_profile.json"), Charsets.UTF_8); + + QuickLinkArtifactResourceProvider provider = createProvider(); + Predicate predicate = new PredicateBuilder().property( + QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN"). + toPredicate(); + Set<Resource> resources = + provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate); + Map<String, Link> linkMap = getLinks(resources); + + for (Map.Entry<String, Link> entry: linkMap.entrySet()) { + assertTrue("Only resourcemanager_ui should be visible.", + entry.getValue().isVisible() == entry.getKey().equals("resourcemanager_ui")); + } + } + + /** + * Test to prove the all links are visible if no profile is set + */ + @Test + public void whenNoProfileIsSetAllLinksAreVisible() throws Exception { + quicklinkProfile = null; + + QuickLinkArtifactResourceProvider provider = createProvider(); + Predicate predicate = new PredicateBuilder().property( + QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN"). + toPredicate(); + Set<Resource> resources = + provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate); + Map<String, Link> linkMap = getLinks(resources); + + for (Link link: linkMap.values()) { + assertTrue("All links should be visible.", link.isVisible()); + } + } + + /** + * Test to prove the all links are visible if invalid profile is set + */ + @Test + public void whenInvalidProfileIsSetAllLinksAreVisible() throws Exception { + quicklinkProfile = "{}"; + + QuickLinkArtifactResourceProvider provider = createProvider(); + Predicate predicate = new PredicateBuilder().property( + QuickLinkArtifactResourceProvider.STACK_NAME_PROPERTY_ID).equals("HDP"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_VERSION_PROPERTY_ID).equals("2.0.6"). + and(). + property(QuickLinkArtifactResourceProvider.STACK_SERVICE_NAME_PROPERTY_ID).equals("YARN"). + toPredicate(); + Set<Resource> resources = + provider.getResources(PropertyHelper.getReadRequest(Sets.<String>newHashSet()), predicate); + Map<String, Link> linkMap = getLinks(resources); + + for (Link link: linkMap.values()) { + assertTrue("All links should be visible.", link.isVisible()); + } + } + + private Map<String, Link> getLinks(Set<Resource> resources) { + QuickLinks quickLinks = (QuickLinks) + resources.iterator().next().getPropertiesMap().get("QuickLinkInfo/quicklink_data").values().iterator().next(); + Map<String, Link> linksMap = new HashMap<>(); + for (Link link: quickLinks.getQuickLinksConfiguration().getLinks()) { + linksMap.put(link.getName(), link); + } + return linksMap; + } + + private class MockModule implements Module { + @Override + public void configure(Binder binder) { + AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class); + StackInfo stack = createMock(StackInfo.class); + ServiceInfo service = createMock(ServiceInfo.class); + + QuickLinksConfigurationInfo qlConfigInfo = new QuickLinksConfigurationInfo(); + qlConfigInfo.setDeleted(false); + qlConfigInfo.setFileName("parent_quicklinks.json"); + qlConfigInfo.setIsDefault(true); + File qlFile = new File(Resources.getResource("parent_quicklinks.json").getFile()) ; + QuickLinksConfigurationModule module = new QuickLinksConfigurationModule(qlFile, qlConfigInfo); + module.getModuleInfo(); + + AmbariManagementController amc = createMock(AmbariManagementController.class); + expect(amc.getAmbariMetaInfo()).andReturn(metaInfo).anyTimes(); + expect(amc.getQuicklinkVisibilityController()).andAnswer( + new IAnswer<QuickLinkVisibilityController>() { + @Override + public QuickLinkVisibilityController answer() throws Throwable { + return QuickLinkVisibilityControllerFactory.get(quicklinkProfile); + } + } + ).anyTimes(); + + try { + expect(metaInfo.getStack(anyString(), anyString())).andReturn(stack).anyTimes(); + } + catch (AmbariException ex) { + throw new RuntimeException(ex); + } + expect(stack.getServices()).andReturn(ImmutableList.of(service)).anyTimes(); + expect(stack.getService("YARN")).andReturn(service).anyTimes(); + expect(service.getQuickLinksConfigurationsMap()).andReturn(ImmutableMap.of("YARN", qlConfigInfo)); + expect(service.getName()).andReturn("YARN").anyTimes(); + binder.bind(AmbariManagementController.class).toInstance(amc); + replay(amc, metaInfo, stack, service); + + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java deleted file mode 100644 index f54842d..0000000 --- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/EvaluatorTest.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ambari.server.state.quicklinksprofile; - -import static org.apache.ambari.server.state.quicklinksprofile.Filter.acceptAllFilter; -import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkAttributeFilter; -import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkNameFilter; -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.ambari.server.state.quicklinks.Link; -import org.junit.Test; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -public class EvaluatorTest { - - static final String NAMENODE = "NAMENODE"; - static final String NAMENODE_UI = "namenode_ui"; - static final String AUTHENTICATED = "authenticated"; - static final String NAMENODE_JMX = "namenode_jmx"; - static final String SSO = "sso"; - - private Link namenodeUi; - private Link nameNodeJmx; - - public EvaluatorTest() { - namenodeUi = new Link(); - namenodeUi.setComponentName(NAMENODE); - namenodeUi.setName(NAMENODE_UI); - namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED)); - - nameNodeJmx = new Link(); - nameNodeJmx.setComponentName(NAMENODE); - nameNodeJmx.setName(NAMENODE_JMX); - } - - /** - * Evaluators should work when initialized with {@code null} or an empty list of filters. - */ - @Test - public void testWithEmptyFilters() throws Exception { - Evaluator evaluator = new Evaluator(new ArrayList<Filter>()); - assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi)); - - Evaluator evaluator2 = new Evaluator(null); - assertEquals(Optional.absent(), evaluator2.isVisible(namenodeUi)); - } - - /** - * Evaluator should return {@link Optional#absent()} when the link doesn't match any filters - */ - @Test - public void testNoMatchingFilter() throws Exception { - List<Filter> filters = Lists.newArrayList( - linkNameFilter(NAMENODE_JMX, true), - linkAttributeFilter(SSO, false)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi)); - } - - /** - * Link name filters should be evaluated first - */ - @Test - public void testLinkNameFiltersEvaluatedFirst() throws Exception { - List<Filter> filters = Lists.newArrayList( - acceptAllFilter(false), - linkNameFilter(NAMENODE_UI, true), - linkNameFilter(NAMENODE_JMX, false), - linkAttributeFilter(AUTHENTICATED, false), - linkAttributeFilter(SSO, false)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); - } - - /** - * Link attribute filters should be evaluated only if the link does not match any link name filters. - */ - @Test - public void testLinkAttributeFiltersEvaluatedSecondly() throws Exception { - List<Filter> filters = Lists.newArrayList( - acceptAllFilter(false), - linkNameFilter(NAMENODE_JMX, false), - linkAttributeFilter(AUTHENTICATED, true), - linkAttributeFilter(SSO, true)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); - } - - /** - * Link attribute filters work with links with null attributes. (No NPE is thrown) - */ - @Test - public void testLinkAttributeFiltersWorkWithNullAttributes() throws Exception { - List<Filter> filters = Lists.newArrayList( - acceptAllFilter(true), - linkAttributeFilter(AUTHENTICATED, false), - linkAttributeFilter(SSO, false)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.of(true), evaluator.isVisible(nameNodeJmx)); - } - - - /** - * If the link matches both a show and hide type link attribute filter, then it will be evaluated as hidden. - */ - @Test - public void testHideFilterTakesPrecedence() throws Exception { - List<Filter> filters = Lists.<Filter>newArrayList( - linkAttributeFilter(AUTHENTICATED, false), - linkAttributeFilter(SSO, true)); - Evaluator evaluator = new Evaluator(filters); - namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED, SSO)); - assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi)); - } - - /** - * Accept-all filters are only evaluated if the link does not match any link name or link attribute filters. - */ - @Test - public void acceptAllFilterEvaluatedLast() throws Exception { - List<Filter> filters = Lists.newArrayList( - acceptAllFilter(false), - linkNameFilter(NAMENODE_JMX, true), - linkAttributeFilter(SSO, true)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi)); - } - - /** - * Contradicting link name filters should result in {@link QuickLinksProfileEvaluatorException}. - */ - @Test(expected = QuickLinksProfileEvaluatorException.class) - public void contradictingLinkNameFiltersRejected() throws Exception { - List<Filter> filters = Lists.newArrayList( - linkNameFilter(NAMENODE_JMX, true), - linkNameFilter(NAMENODE_JMX, false), - linkAttributeFilter(SSO, true)); - new Evaluator(filters); - } - - /** - * Contradicting link attribute filters should result in {@link QuickLinksProfileEvaluatorException}. - */ - @Test(expected = QuickLinksProfileEvaluatorException.class) - public void contradictingLinkAttributeFiltersRejected() throws Exception { - List<Filter> filters = Lists.<Filter>newArrayList( - linkAttributeFilter(SSO, true), - linkAttributeFilter(SSO, false)); - new Evaluator(filters); - } - - /** - * Contradicting accept-all filters should result in {@link QuickLinksProfileEvaluatorException}. - */ - @Test(expected = QuickLinksProfileEvaluatorException.class) - public void contradictingAcceptAllFiltersRejected() throws Exception { - List<Filter> filters = Lists.newArrayList( - linkNameFilter(NAMENODE_JMX, true), - linkAttributeFilter(SSO, true), - acceptAllFilter(true), - acceptAllFilter(false)); - new Evaluator(filters); - } - - /** - * Duplicate filter declarations are ok if their visibility rule is the same - */ - @Test - public void duplicateFiltersAreOkIfDoNotContradict() throws Exception { - List<Filter> filters = Lists.newArrayList( - acceptAllFilter(true), - acceptAllFilter(true), - linkNameFilter(NAMENODE_JMX, false), - linkNameFilter(NAMENODE_JMX, false), - linkAttributeFilter(SSO, false), - linkAttributeFilter(SSO, false)); - Evaluator evaluator = new Evaluator(filters); - assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java new file mode 100644 index 0000000..8646413 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluatorTest.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +import static org.apache.ambari.server.state.quicklinksprofile.Filter.acceptAllFilter; +import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkAttributeFilter; +import static org.apache.ambari.server.state.quicklinksprofile.Filter.linkNameFilter; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.ambari.server.state.quicklinks.Link; +import org.junit.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class FilterEvaluatorTest { + + static final String NAMENODE = "NAMENODE"; + static final String NAMENODE_UI = "namenode_ui"; + static final String AUTHENTICATED = "authenticated"; + static final String NAMENODE_JMX = "namenode_jmx"; + static final String SSO = "sso"; + + private Link namenodeUi; + private Link nameNodeJmx; + + public FilterEvaluatorTest() { + namenodeUi = new Link(); + namenodeUi.setComponentName(NAMENODE); + namenodeUi.setName(NAMENODE_UI); + namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED)); + + // this is a "legacy" link with no attributes defined + nameNodeJmx = new Link(); + nameNodeJmx.setComponentName(NAMENODE); + nameNodeJmx.setName(NAMENODE_JMX); + } + + /** + * Evaluators should work when initialized with {@code null} or an empty list of filters. + */ + @Test + public void testWithEmptyFilters() throws Exception { + FilterEvaluator evaluator = new FilterEvaluator(new ArrayList<Filter>()); + assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi)); + + FilterEvaluator evaluator2 = new FilterEvaluator(null); + assertEquals(Optional.absent(), evaluator2.isVisible(namenodeUi)); + } + + /** + * FilterEvaluator should return {@link Optional#absent()} when the link doesn't match any filters + */ + @Test + public void testNoMatchingFilter() throws Exception { + List<Filter> filters = Lists.newArrayList( + linkNameFilter(NAMENODE_JMX, true), + linkAttributeFilter(SSO, false)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi)); + } + + /** + * Link name filters should be evaluated first + */ + @Test + public void testLinkNameFiltersEvaluatedFirst() throws Exception { + List<Filter> filters = Lists.newArrayList( + acceptAllFilter(false), + linkNameFilter(NAMENODE_UI, true), + linkNameFilter(NAMENODE_JMX, false), + linkAttributeFilter(AUTHENTICATED, false), + linkAttributeFilter(SSO, false)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); + } + + /** + * Link attribute filters should be evaluated only if the link does not match any link name filters. + */ + @Test + public void testLinkAttributeFiltersEvaluatedSecondly() throws Exception { + List<Filter> filters = Lists.newArrayList( + acceptAllFilter(false), + linkNameFilter(NAMENODE_JMX, false), + linkAttributeFilter(AUTHENTICATED, true), + linkAttributeFilter(SSO, true)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); + } + + + /** + * Link attribute filters work with links with null attributes. (No NPE is thrown) + */ + @Test + public void testLinkAttributeFiltersWorkWithNullAttributes() throws Exception { + List<Filter> filters = Lists.newArrayList( + acceptAllFilter(true), + linkAttributeFilter(AUTHENTICATED, false), + linkAttributeFilter(SSO, false)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.of(true), evaluator.isVisible(nameNodeJmx)); + } + + + /** + * If the link matches both a show and hide type link attribute filter, then it will be evaluated as hidden. + */ + @Test + public void testHideFilterTakesPrecedence() throws Exception { + List<Filter> filters = Lists.<Filter>newArrayList( + linkAttributeFilter(AUTHENTICATED, false), + linkAttributeFilter(SSO, true)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED, SSO)); + assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi)); + } + + /** + * Accept-all filters are only evaluated if the link does not match any link name or link attribute filters. + */ + @Test + public void acceptAllFilterEvaluatedLast() throws Exception { + List<Filter> filters = Lists.newArrayList( + acceptAllFilter(false), + linkNameFilter(NAMENODE_JMX, true), + linkAttributeFilter(SSO, true)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.of(false), evaluator.isVisible(namenodeUi)); + } + + /** + * Contradicting link name filters should result in {@link QuickLinksProfileEvaluationException}. + */ + @Test(expected = QuickLinksProfileEvaluationException.class) + public void contradictingLinkNameFiltersRejected() throws Exception { + List<Filter> filters = Lists.newArrayList( + linkNameFilter(NAMENODE_JMX, true), + linkNameFilter(NAMENODE_JMX, false), + linkAttributeFilter(SSO, true)); + new FilterEvaluator(filters); + } + + /** + * Contradicting property filters should result in {@link QuickLinksProfileEvaluationException}. + * @throws Exception + */ + @Test(expected = QuickLinksProfileEvaluationException.class) + public void contradictingPropertyFiltersRejected() throws Exception { + List<Filter> filters = Lists.<Filter>newArrayList( + linkAttributeFilter(SSO, true), + linkAttributeFilter(SSO, false)); + new FilterEvaluator(filters); + } + + + /** + * Contradicting link attribute filters should result in {@link QuickLinksProfileEvaluationException}. + */ + @Test(expected = QuickLinksProfileEvaluationException.class) + public void contradictingLinkAttributeFiltersRejected() throws Exception { + List<Filter> filters = Lists.<Filter>newArrayList( + linkAttributeFilter(SSO, true), + linkAttributeFilter(SSO, false)); + new FilterEvaluator(filters); + } + + /** + * Contradicting accept-all filters should result in {@link QuickLinksProfileEvaluationException}. + */ + @Test(expected = QuickLinksProfileEvaluationException.class) + public void contradictingAcceptAllFiltersRejected() throws Exception { + List<Filter> filters = Lists.newArrayList( + linkNameFilter(NAMENODE_JMX, true), + linkAttributeFilter(SSO, true), + acceptAllFilter(true), + acceptAllFilter(false)); + new FilterEvaluator(filters); + } + + /** + * Duplicate filter declarations are ok if their visibility rule is the same + */ + @Test + public void duplicateFiltersAreOkIfDoNotContradict() throws Exception { + List<Filter> filters = Lists.newArrayList( + acceptAllFilter(true), + acceptAllFilter(true), + linkNameFilter(NAMENODE_JMX, false), + linkNameFilter(NAMENODE_JMX, false), + linkAttributeFilter(SSO, false), + linkAttributeFilter(SSO, false)); + FilterEvaluator evaluator = new FilterEvaluator(filters); + assertEquals(Optional.of(true), evaluator.isVisible(namenodeUi)); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java new file mode 100644 index 0000000..c278791 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerFactoryTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; + + +public class QuickLinkVisibilityControllerFactoryTest { + + /** + * If receives a {@code null} quick profile string, the factory should return an instance of {@link ShowAllLinksVisibilityController} + * to mimic legacy behavior. + */ + @Test + public void nullStringPassed() throws Exception { + assertTrue( + "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned", + QuickLinkVisibilityControllerFactory.get(null) instanceof ShowAllLinksVisibilityController); + } + + /** + * If receives an invalid json as quick profile string, the factory should return an instance of {@link ShowAllLinksVisibilityController} + * to mimic legacy behavior. + */ + @Test + public void invalidJsonPassed() throws Exception { + assertTrue( + "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned", + QuickLinkVisibilityControllerFactory.get("Hello world!") instanceof ShowAllLinksVisibilityController); + } + + /** + * If receives an invalid quick profile string (e.g. valid json but no filters defined or contradicting filters), + * the factory should return an instance of {@link ShowAllLinksVisibilityController} to mimic legacy behavior. + */ + @Test + public void invalidProfilePassed() throws Exception { + String json = Resources.toString(Resources.getResource("inconsistent_quicklinks_profile.json"), Charsets.UTF_8); + assertTrue( + "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned", + QuickLinkVisibilityControllerFactory.get(json) instanceof ShowAllLinksVisibilityController); + + json = Resources.toString(Resources.getResource("inconsistent_quicklinks_profile_2.json"), Charsets.UTF_8); + assertTrue( + "An instance of " + ShowAllLinksVisibilityController.class.getSimpleName() + " should have been returned", + QuickLinkVisibilityControllerFactory.get(json) instanceof ShowAllLinksVisibilityController); + } + + /** + * If receives a valid profile json string, an visibility controller should be returned that behaves according to the + * received profile. + */ + @Test + public void validProfilePassed() throws Exception { + String json = Resources.toString(Resources.getResource("example_quicklinks_profile.json"), Charsets.UTF_8); + assertTrue( + "An instance of " + DefaultQuickLinkVisibilityController.class.getSimpleName() + " should have been returned", + QuickLinkVisibilityControllerFactory.get(json) instanceof DefaultQuickLinkVisibilityController); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java new file mode 100644 index 0000000..9e44e45 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityControllerTest.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.state.quicklinksprofile; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.apache.ambari.server.state.quicklinks.Link; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +public class QuickLinkVisibilityControllerTest { + + static final String AUTHENTICATED = "authenticated"; + static final String SSO = "sso"; + static final String NAMENODE = "NAMENODE"; + static final String HDFS = "HDFS"; + static final String NAMENODE_UI = "namenode_ui"; + + + private Link namenodeUi; + + public QuickLinkVisibilityControllerTest() { + namenodeUi = new Link(); + namenodeUi.setComponentName(NAMENODE); + namenodeUi.setName(NAMENODE_UI); + namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED)); + } + + /** + * Test to prove that {@link DefaultQuickLinkVisibilityController} can accept quicklink profiles with null values. + */ + @Test + public void testNullsAreAccepted() throws Exception { + QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), null); + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + evaluator.isVisible(HDFS, namenodeUi); //should not throw NPE + + Service service = Service.create(HDFS, ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), null); + profile = QuickLinksProfile.create(null, ImmutableList.of(service)); + evaluator = new DefaultQuickLinkVisibilityController(profile); + evaluator.isVisible(HDFS, namenodeUi); //should not throw NPE + + } + + /** + * Quicklinks profile must contain at least one filter (can be on any level: global/component/service), otherwise + * an exception is thrown. + */ + @Test(expected = QuickLinksProfileEvaluationException.class) + public void testProfileMustContainAtLeastOneFilter() throws Exception { + Component component = Component.create("NAMENODE", null); + Service service = Service.create(HDFS, null, ImmutableList.of(component)); + QuickLinksProfile profile = QuickLinksProfile.create(null, ImmutableList.of(service)); + QuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + } + + /** + * Test to prove that {@link Link}'s with unset {@code componentName} fields are handled properly. + */ + @Test + public void testLinkWithNoComponentField() throws Exception { + Component component = Component.create(NAMENODE, + ImmutableList.<Filter>of(Filter.linkNameFilter(NAMENODE_UI, true))); + + Service service = Service.create(HDFS, ImmutableList.<Filter>of(), ImmutableList.of(component)); + + QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(), ImmutableList.of(service)); + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + namenodeUi.setComponentName(null); + assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi)); + } + + /** + * Test to prove that component level filters are evaluated first. + */ + @Test + public void testComponentLevelFiltersEvaluatedFirst() throws Exception { + Component component = Component.create( + NAMENODE, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true))); + + Service service = Service.create( + HDFS, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, false)), + ImmutableList.of(component)); + + QuickLinksProfile profile = QuickLinksProfile.create( + ImmutableList.<Filter>of(Filter.acceptAllFilter(false)), + ImmutableList.of(service)); + + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); + } + + /** + * Test to prove that service level filters are evaluated secondly. + */ + @Test + public void testServiceLevelFiltersEvaluatedSecondly() throws Exception { + Component component = Component.create(NAMENODE, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false))); + + Service service = Service.create(HDFS, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)), + ImmutableList.of(component)); + + QuickLinksProfile profile = QuickLinksProfile.create( + ImmutableList.<Filter>of(Filter.acceptAllFilter(false)), + ImmutableList.of(service)); + + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); + } + + /** + * Test to prove that global filters are evaluated last. + */ + @Test + public void testGlobalFiltersEvaluatedLast() throws Exception { + Component component = Component.create(NAMENODE, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false))); + + Service service = Service.create(HDFS, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)), + ImmutableList.of(component)); + + QuickLinksProfile profile = QuickLinksProfile.create( + ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), + ImmutableList.of(service)); + + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + assertTrue("Global filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); + } + + /** + * Test to prove that the link is hidden if no filters apply. + */ + @Test + public void testNoMatchingRule() throws Exception { + Component component1 = Component.create(NAMENODE, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true))); + + Component component2 = Component.create("DATANODE", + ImmutableList.<Filter>of(Filter.acceptAllFilter(true))); + + Service service1 = Service.create(HDFS, + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)), + ImmutableList.of(component1, component2)); + + Service service2 = Service.create("YARN", + ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), + ImmutableList.<Component>of()); + + QuickLinksProfile profile = QuickLinksProfile.create( + ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)), + ImmutableList.of(service1, service2)); + + DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile); + assertFalse("No filters should have been applied, so default false should have been returned.", + evaluator.isVisible(HDFS, namenodeUi)); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/2643dd65/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java deleted file mode 100644 index 6a31ca0..0000000 --- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluatorTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ambari.server.state.quicklinksprofile; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.apache.ambari.server.state.quicklinks.Link; -import org.junit.Test; - -import com.google.common.collect.ImmutableList; - -public class QuickLinksProfileEvaluatorTest { - - static final String AUTHENTICATED = "authenticated"; - static final String SSO = "sso"; - static final String NAMENODE = "NAMENODE"; - static final String HDFS = "HDFS"; - static final String NAMENODE_UI = "namenode_ui"; - - - private Link namenodeUi; - - public QuickLinksProfileEvaluatorTest() { - namenodeUi = new Link(); - namenodeUi.setComponentName(NAMENODE); - namenodeUi.setName(NAMENODE_UI); - namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED)); - } - - /** - * Test to prove that {@link QuickLinksProfileEvaluator} can accept quicklink profiles with null values. - */ - @Test - public void testNullsAreAccepted() throws Exception { - QuickLinksProfile profile = new QuickLinksProfile(); - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi)); - - Service service = Service.create(HDFS, null, null); - profile = QuickLinksProfile.create(null, ImmutableList.of(service)); - evaluator = new QuickLinksProfileEvaluator(profile); - assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi)); - } - - /** - * Test to prove that {@link Link}'s with unset {@code componentName} fields are handled properly. - */ - @Test - public void testLinkWithNoComponentField() throws Exception { - Component component = Component.create(NAMENODE, - ImmutableList.<Filter>of(Filter.linkNameFilter(NAMENODE_UI, true))); - - Service service = Service.create(HDFS, ImmutableList.<Filter>of(), ImmutableList.of(component)); - - QuickLinksProfile profile = QuickLinksProfile.create(ImmutableList.<Filter>of(), ImmutableList.of(service)); - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - namenodeUi.setComponentName(null); - assertFalse("Link should be hidden as there are no applicable filters", evaluator.isVisible(HDFS, namenodeUi)); - } - - /** - * Test to prove that component level filters are evaluated first. - */ - @Test - public void testComponentLevelFiltersEvaluatedFirst() throws Exception { - Component component = Component.create( - NAMENODE, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true))); - - Service service = Service.create( - HDFS, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, false)), - ImmutableList.of(component)); - - QuickLinksProfile profile = QuickLinksProfile.create( - ImmutableList.<Filter>of(Filter.acceptAllFilter(false)), - ImmutableList.of(service)); - - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); - } - - /** - * Test to prove that service level filters are evaluated secondly. - */ - @Test - public void testServiceLevelFiltersEvaluatedSecondly() throws Exception { - Component component = Component.create(NAMENODE, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false))); - - Service service = Service.create(HDFS, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(AUTHENTICATED, true)), - ImmutableList.of(component)); - - QuickLinksProfile profile = QuickLinksProfile.create( - ImmutableList.<Filter>of(Filter.acceptAllFilter(false)), - ImmutableList.of(service)); - - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - assertTrue("Component level filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); - } - - /** - * Test to prove that global filters are evaluated last. - */ - @Test - public void testGlobalFiltersEvaluatedLast() throws Exception { - Component component = Component.create(NAMENODE, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false))); - - Service service = Service.create(HDFS, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, false)), - ImmutableList.of(component)); - - QuickLinksProfile profile = QuickLinksProfile.create( - ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), - ImmutableList.of(service)); - - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - assertTrue("Global filter should have been applied.", evaluator.isVisible(HDFS, namenodeUi)); - } - - /** - * Test to prove that the link is hidden if no filters apply. - */ - @Test - public void testNoMatchingRule() throws Exception { - Component component1 = Component.create(NAMENODE, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true))); - - Component component2 = Component.create("DATANODE", - ImmutableList.<Filter>of(Filter.acceptAllFilter(true))); - - Service service1 = Service.create(HDFS, - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)), - ImmutableList.of(component1, component2)); - - Service service2 = Service.create("YARN", - ImmutableList.<Filter>of(Filter.acceptAllFilter(true)), - ImmutableList.<Component>of()); - - QuickLinksProfile profile = QuickLinksProfile.create( - ImmutableList.<Filter>of(Filter.linkAttributeFilter(SSO, true)), - ImmutableList.of(service1, service2)); - - QuickLinksProfileEvaluator evaluator = new QuickLinksProfileEvaluator(profile); - assertFalse("No filters should have been applied, so default false should have been returned.", - evaluator.isVisible(HDFS, namenodeUi)); - } - -} \ No newline at end of file