You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by be...@apache.org on 2018/11/22 11:21:06 UTC
[ambari] branch trunk updated: [AMBARI-24938] quick link profiles
can override link url (benyoka) (#2646)
This is an automated email from the ASF dual-hosted git repository.
benyoka pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new 8079522 [AMBARI-24938] quick link profiles can override link url (benyoka) (#2646)
8079522 is described below
commit 8079522317ed9e1f37b913946b97f239ae965ab7
Author: benyoka <be...@users.noreply.github.com>
AuthorDate: Thu Nov 22 12:21:01 2018 +0100
[AMBARI-24938] quick link profiles can override link url (benyoka) (#2646)
* AMBARI-24938 quick link profiles can override link url (benyoka)
* AMBARI-24938 new unit test in QuickLinkArtifactResourceProviderTest (benyoka)
* AMBARI-24938 review findings + check style issue + unit test fix (benyoka)
---
.../QuickLinkArtifactResourceProvider.java | 5 +-
.../state/quicklinksprofile/AcceptAllFilter.java | 1 +
.../server/state/quicklinksprofile/Component.java | 13 +-
.../DefaultQuickLinkVisibilityController.java | 181 ++++++++-------------
.../server/state/quicklinksprofile/Filter.java | 11 +-
...ibilityController.java => FilterEvaluator.java} | 115 +------------
.../quicklinksprofile/LinkAttributeFilter.java | 11 +-
.../state/quicklinksprofile/LinkNameFilter.java | 49 +++++-
.../QuickLinkVisibilityController.java | 9 +
.../state/quicklinksprofile/QuickLinksProfile.java | 14 +-
.../QuickLinksProfileBuilder.java | 10 +-
.../quicklinksprofile/QuickLinksProfileParser.java | 39 ++---
.../server/state/quicklinksprofile/Service.java | 15 +-
.../ShowAllLinksVisibilityController.java | 6 +
.../StreamUtils.java} | 26 +--
.../internal/ProvisionClusterRequestTest.java | 6 +-
.../QuickLinkArtifactResourceProviderTest.java | 35 ++--
.../quicklinksprofile/FilterEvaluatorTest.java | 10 +-
.../QuickLinkVisibilityControllerTest.java | 83 +++++++++-
.../QuickLinksProfileBuilderTest.java | 28 +++-
.../QuickLinksProfileParserTest.java | 17 +-
.../test/resources/example_quicklinks_profile.json | 2 +
.../inconsistent_quicklinks_profile_4.json | 10 ++
23 files changed, 375 insertions(+), 321 deletions(-)
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 2d9a256..b381023 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
@@ -179,7 +179,7 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc
}
}
- setVisibility(serviceInfo.getName(), serviceQuickLinks);
+ setVisibilityAndOverrides(serviceInfo.getName(), serviceQuickLinks);
List<Resource> serviceResources = new ArrayList<>();
for (QuickLinksConfigurationInfo quickLinksConfigurationInfo : serviceQuickLinks) {
@@ -206,13 +206,14 @@ public class QuickLinkArtifactResourceProvider extends AbstractControllerResourc
* @param serviceName the name of the service
* @param serviceQuickLinks the links
*/
- private void setVisibility(String serviceName, List<QuickLinksConfigurationInfo> serviceQuickLinks) {
+ private void setVisibilityAndOverrides(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));
+ visibilityController.getUrlOverride(serviceName, link).ifPresent(link::setUrl);
}
}
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
index 069ae3f..01500bf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
@@ -48,4 +48,5 @@ public class AcceptAllFilter extends Filter {
public String toString() {
return getClass().getSimpleName() + " (visible=" + isVisible() + ")";
}
+
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
index 729e5d4..f07a05a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
@@ -18,18 +18,19 @@
package org.apache.ambari.server.state.quicklinksprofile;
-import java.util.List;
+import static java.util.Collections.emptyList;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
/**
* Class to represent component-level filter definitions
*/
-@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Component {
@JsonProperty("name")
@@ -58,7 +59,7 @@ public class Component {
* @return the quicklink filters for this component
*/
public List<Filter> getFilters() {
- return filters;
+ return null != filters ? filters : emptyList();
}
public void setFilters(List<Filter> filters) {
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
index d0b0442..b8f6f1b 100644
--- 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
@@ -18,45 +18,97 @@
package org.apache.ambari.server.state.quicklinksprofile;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toMap;
+
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.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.ambari.server.state.quicklinks.Link;
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
/**
* 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 static final Logger LOG = LoggerFactory.getLogger(DefaultQuickLinkVisibilityController.class);
+
private final FilterEvaluator globalRules;
private final Map<String, FilterEvaluator> serviceRules = new HashMap<>();
- private final Map<ServiceComponent, FilterEvaluator> componentRules = new HashMap<>();
+ /**
+ * Map of (service name, component name) -> filter evaluator
+ */
+ private final Map<Pair<String, String>, FilterEvaluator> componentRules = new HashMap<>();
+ /**
+ * Map of (service name, link name) -> url
+ */
+ private final Map<Pair<String, String>, String> urlOverrides = new HashMap<>();
+
public DefaultQuickLinkVisibilityController(QuickLinksProfile profile) throws QuickLinksProfileEvaluationException {
int filterCount = size(profile.getFilters());
globalRules = new FilterEvaluator(profile.getFilters());
- for (Service service: nullToEmptyList(profile.getServices())) {
+ for (Service service: profile.getServices()) {
filterCount += size(service.getFilters());
serviceRules.put(service.getName(), new FilterEvaluator(service.getFilters()));
- for (Component component: nullToEmptyList(service.getComponents())) {
+ for (Component component: service.getComponents()) {
filterCount += size(component.getFilters());
- componentRules.put(ServiceComponent.of(service.getName(), component.getName()),
+ componentRules.put(Pair.of(service.getName(), component.getName()),
new FilterEvaluator(component.getFilters()));
}
}
if (filterCount == 0) {
throw new QuickLinksProfileEvaluationException("At least one filter must be defined.");
}
+
+ // compute url overrides
+ String globalOverrides = LinkNameFilter.getLinkNameFilters(profile.getFilters().stream())
+ .filter(f -> f.getLinkUrl() != null)
+ .map(f -> f.getLinkName() + " -> " + f.getLinkUrl())
+ .collect(joining(", "));
+ if (!globalOverrides.isEmpty()) {
+ LOG.warn("Link url overrides only work on service and component levels. The following global overrides will be " +
+ "ignored: {}", globalOverrides);
+ }
+ for (Service service : profile.getServices()) {
+ urlOverrides.putAll(getUrlOverrides(service.getName(), service.getFilters()));
+
+ for (Component component : service.getComponents()) {
+ Map<Pair<String, String>, String> componentUrlOverrides = getUrlOverrides(service.getName(), component.getFilters());
+ Set<Pair<String, String>> duplicateOverrides = Sets.intersection(urlOverrides.keySet(), componentUrlOverrides.keySet());
+ if (!duplicateOverrides.isEmpty()) {
+ LOG.warn("Duplicate url overrides in quick links profile: {}", duplicateOverrides);
+ }
+ urlOverrides.putAll(componentUrlOverrides);
+ }
+ }
+ }
+
+ private Map<Pair<String, String>, String> getUrlOverrides(String serviceName, Collection<Filter> filters) {
+ return filters.stream()
+ .filter( f -> f instanceof LinkNameFilter && null != ((LinkNameFilter)f).getLinkUrl() )
+ .map( f -> {
+ LinkNameFilter lnf = (LinkNameFilter)f;
+ return Pair.of(Pair.of(serviceName, lnf.getLinkName()), lnf.getLinkUrl());
+ })
+ .collect( toMap(Pair::getKey, Pair::getValue) );
+ }
+
+ @Override
+ public Optional<String> getUrlOverride(@Nonnull String service, @Nonnull Link quickLink) {
+ return Optional.ofNullable( urlOverrides.get(Pair.of(service, quickLink.getName())) );
}
/**
@@ -78,7 +130,7 @@ public class DefaultQuickLinkVisibilityController implements QuickLinkVisibility
}
// Global rules are evaluated lastly. If no rules apply to the link, it will be hidden.
- return globalRules.isVisible(quickLink).or(false);
+ return globalRules.isVisible(quickLink).orElse(false);
}
private int size(@Nullable Collection<?> collection) {
@@ -87,127 +139,22 @@ public class DefaultQuickLinkVisibilityController implements QuickLinkVisibility
private Optional<Boolean> evaluateComponentRules(@Nonnull String service, @Nonnull Link quickLink) {
if (null == quickLink.getComponentName()) {
- return Optional.absent();
+ return Optional.empty();
}
else {
- FilterEvaluator componentEvaluator = componentRules.get(ServiceComponent.of(service, quickLink.getComponentName()));
- return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.absent();
+ FilterEvaluator componentEvaluator = componentRules.get(Pair.of(service, quickLink.getComponentName()));
+ return componentEvaluator != null ? componentEvaluator.isVisible(quickLink) : Optional.empty();
}
}
private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) {
return serviceRules.containsKey(service) ?
- serviceRules.get(service).isVisible(quickLink) : Optional.absent();
+ serviceRules.get(service).isVisible(quickLink) : Optional.empty();
}
static <T> List<T> nullToEmptyList(@Nullable List<T> items) {
return items != null ? items : Collections.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
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
index 26410a5..2b54e2f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
@@ -19,10 +19,10 @@
package org.apache.ambari.server.state.quicklinksprofile;
import org.apache.ambari.server.state.quicklinks.Link;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.base.Preconditions;
/**
@@ -66,9 +66,14 @@ public abstract class Filter {
}
static LinkNameFilter linkNameFilter(String linkName, boolean visible) {
+ return linkNameFilter(linkName, null, visible);
+ }
+
+ static LinkNameFilter linkNameFilter(String linkName, String linkUrl, boolean visible) {
Preconditions.checkNotNull(linkName, "Link name must not be null");
LinkNameFilter linkNameFilter = new LinkNameFilter();
linkNameFilter.setLinkName(linkName);
+ linkNameFilter.setLinkUrl(linkUrl);
linkNameFilter.setVisible(visible);
return linkNameFilter;
}
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/FilterEvaluator.java
similarity index 51%
copy from ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/DefaultQuickLinkVisibilityController.java
copy to ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/FilterEvaluator.java
index d0b0442..5b6124a 100644
--- 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/FilterEvaluator.java
@@ -18,97 +18,19 @@
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.Optional;
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.absent();
- }
- }
-
- private Optional<Boolean> evaluateServiceRules(@Nonnull String service, @Nonnull Link quickLink) {
- return serviceRules.containsKey(service) ?
- serviceRules.get(service).isVisible(quickLink) : Optional.absent();
- }
-
- static <T> List<T> nullToEmptyList(@Nullable List<T> items) {
- return items != null ? items : Collections.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()}.
+ * returns {@link Optional#empty()}.
* <p>
* Filter evaluation order is the following:
* <ol>
@@ -123,7 +45,7 @@ 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();
+ private Optional<Boolean> acceptAllFilter = Optional.empty();
FilterEvaluator(List<Filter> filters) throws QuickLinksProfileEvaluationException {
for (Filter filter: DefaultQuickLinkVisibilityController.nullToEmptyList(filters)) {
@@ -180,34 +102,3 @@ class FilterEvaluator {
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
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkAttributeFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkAttributeFilter.java
index b10111d..d13b3d5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkAttributeFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkAttributeFilter.java
@@ -21,7 +21,9 @@ package org.apache.ambari.server.state.quicklinksprofile;
import java.util.Objects;
import org.apache.ambari.server.state.quicklinks.Link;
-import org.codehaus.jackson.annotate.JsonProperty;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
/**
* A quicklink filter based on link attribute match (the filter's link_attribute is contained by the links set of
@@ -58,4 +60,11 @@ public class LinkAttributeFilter extends Filter {
public int hashCode() {
return Objects.hash(isVisible(), linkAttribute);
}
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("linkAttribute", linkAttribute)
+ .toString();
+ }
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
index b874295..e9d0521 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/LinkNameFilter.java
@@ -19,28 +19,56 @@
package org.apache.ambari.server.state.quicklinksprofile;
import java.util.Objects;
+import java.util.stream.Stream;
+
+import javax.annotation.Nullable;
import org.apache.ambari.server.state.quicklinks.Link;
-import org.codehaus.jackson.annotate.JsonProperty;
+import org.apache.ambari.server.utils.StreamUtils;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.MoreObjects;
+
/**
* A filter that accepts quicklinks based on name match.
*/
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class LinkNameFilter extends Filter {
static final String LINK_NAME = "link_name";
+ static final String LINK_URL = "link_url";
@JsonProperty(LINK_NAME)
private String linkName;
+ /**
+ * In addition to filtering this filter allows overriding the link url too.
+ */
+ @JsonProperty(LINK_URL)
+ private String linkUrl;
+
+ @JsonProperty(LINK_NAME)
public String getLinkName() {
return linkName;
}
+ @JsonProperty(LINK_NAME)
public void setLinkName(String linkName) {
this.linkName = linkName;
}
+ @JsonProperty(LINK_URL)
+ public @Nullable String getLinkUrl() {
+ return linkUrl;
+ }
+
+ @JsonProperty(LINK_URL)
+ public void setLinkUrl(@Nullable String linkUrl) {
+ this.linkUrl = linkUrl;
+ }
+
@Override
public boolean accept(Link link) {
return Objects.equals(link.getName(), linkName);
@@ -51,11 +79,26 @@ public class LinkNameFilter extends Filter {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LinkNameFilter that = (LinkNameFilter) o;
- return isVisible() == that.isVisible() && Objects.equals(linkName, that.linkName);
+ return Objects.equals(linkName, that.linkName) &&
+ Objects.equals(linkUrl, that.linkUrl) &&
+ isVisible() == that.isVisible();
}
@Override
public int hashCode() {
- return Objects.hash(isVisible(), linkName);
+ return Objects.hash(linkName, linkUrl);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("linkName", linkName)
+ .add("linkUrl", linkUrl)
+ .add("visible", isVisible())
+ .toString();
+ }
+
+ static Stream<LinkNameFilter> getLinkNameFilters(Stream<Filter> input) {
+ return StreamUtils.instancesOf(input, LinkNameFilter.class);
}
}
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
index caa2e2e..4f137e3 100644
--- 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
@@ -19,6 +19,8 @@
package org.apache.ambari.server.state.quicklinksprofile;
+import java.util.Optional;
+
import javax.annotation.Nonnull;
import org.apache.ambari.server.state.quicklinks.Link;
@@ -33,5 +35,12 @@ public interface QuickLinkVisibilityController {
*/
boolean isVisible(@Nonnull String service, @Nonnull Link quickLink);
+ /**
+ * @param service The name of the service the quicklink belongs to
+ * @param quickLink the link
+ * @return An optional url override for this link
+ */
+ Optional<String> getUrlOverride(@Nonnull String service, @Nonnull Link quickLink);
+
}
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 7d480e9..9382dab 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
@@ -18,11 +18,13 @@
package org.apache.ambari.server.state.quicklinksprofile;
+import static java.util.Collections.emptyList;
+
import java.util.List;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A quicklinks profile is essentially a set of quick link filters defined on three levels:
@@ -37,7 +39,7 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
* 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)
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class QuickLinksProfile {
@@ -67,7 +69,7 @@ public class QuickLinksProfile {
* @return service-specific quicklink filter definitions
*/
public List<Service> getServices() {
- return services;
+ return services != null ? services : emptyList();
}
public void setServices(List<Service> services) {
@@ -78,7 +80,7 @@ public class QuickLinksProfile {
* @return the global quicklink filters
*/
public List<Filter> getFilters() {
- return filters;
+ return null != filters ? filters : emptyList();
}
public void setFilters(List<Filter> filters) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
index 627b1bc..9806caf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
@@ -21,6 +21,7 @@ package org.apache.ambari.server.state.quicklinksprofile;
import static org.apache.ambari.server.state.quicklinksprofile.Filter.VISIBLE;
import static org.apache.ambari.server.state.quicklinksprofile.LinkAttributeFilter.LINK_ATTRIBUTE;
import static org.apache.ambari.server.state.quicklinksprofile.LinkNameFilter.LINK_NAME;
+import static org.apache.ambari.server.state.quicklinksprofile.LinkNameFilter.LINK_URL;
import java.util.ArrayList;
import java.util.Collection;
@@ -44,7 +45,7 @@ public class QuickLinksProfileBuilder {
public static final String COMPONENTS = "components";
public static final String FILTERS = "filters";
public static final Set<String> ALLOWED_FILTER_ATTRIBUTES =
- ImmutableSet.of(VISIBLE, LINK_NAME, LINK_ATTRIBUTE);
+ ImmutableSet.of(VISIBLE, LINK_NAME, LINK_URL, LINK_ATTRIBUTE);
/**
*
@@ -116,6 +117,7 @@ public class QuickLinksProfileBuilder {
invalidAttributes);
String linkName = filterAsMap.get(LINK_NAME);
+ String linkUrl = filterAsMap.get(LINK_URL);
String attributeName = filterAsMap.get(LINK_ATTRIBUTE);
boolean visible = Boolean.parseBoolean(filterAsMap.get(VISIBLE));
@@ -125,8 +127,12 @@ public class QuickLinksProfileBuilder {
linkName,
attributeName);
+ Preconditions.checkArgument(null == linkUrl || null != linkName,
+ "Invalid filter. Link url can only be applied to link name filters. link_url: %s",
+ linkUrl);
+
if (null != linkName) {
- filters.add(Filter.linkNameFilter(linkName, visible));
+ filters.add(Filter.linkNameFilter(linkName, linkUrl, visible));
}
else if (null != attributeName) {
filters.add(Filter.linkAttributeFilter(attributeName, visible));
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
index 1891061..0e0f8b5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
@@ -23,16 +23,16 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
-import org.codehaus.jackson.JsonParseException;
-import org.codehaus.jackson.JsonParser;
-import org.codehaus.jackson.JsonProcessingException;
-import org.codehaus.jackson.Version;
-import org.codehaus.jackson.map.DeserializationContext;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.map.deser.std.StdDeserializer;
-import org.codehaus.jackson.map.module.SimpleModule;
-import org.codehaus.jackson.node.ObjectNode;
-
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources;
@@ -44,12 +44,12 @@ public class QuickLinksProfileParser {
public QuickLinksProfileParser() {
SimpleModule module =
- new SimpleModule("Quick Links Parser", new Version(1, 0, 0, null));
+ new SimpleModule("Quick Links Parser", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Filter.class, new QuickLinksFilterDeserializer());
mapper.registerModule(module);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
}
-
public QuickLinksProfile parse(byte[] input) throws IOException {
return mapper.readValue(input, QuickLinksProfile.class);
}
@@ -91,20 +91,21 @@ class QuickLinksFilterDeserializer extends StdDeserializer<Filter> {
@Override
public Filter deserialize (JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
- ObjectNode root = (ObjectNode) mapper.readTree(parser);
+ ObjectNode root = mapper.readTree(parser);
Class<? extends Filter> filterClass = null;
List<String> invalidAttributes = new ArrayList<>();
- for (String fieldName: ImmutableList.copyOf(root.getFieldNames())) {
+ for (String fieldName: ImmutableList.copyOf(root.fieldNames())) {
switch(fieldName) {
case LinkAttributeFilter.LINK_ATTRIBUTE:
if (null != filterClass) {
- throw new JsonParseException(PARSE_ERROR_MESSAGE_AMBIGUOUS_FILTER, parser.getCurrentLocation());
+ throw new JsonParseException(parser, PARSE_ERROR_MESSAGE_AMBIGUOUS_FILTER, parser.getCurrentLocation());
}
filterClass = LinkAttributeFilter.class;
break;
case LinkNameFilter.LINK_NAME:
- if (null != filterClass) {
- throw new JsonParseException(PARSE_ERROR_MESSAGE_AMBIGUOUS_FILTER, parser.getCurrentLocation());
+ case LinkNameFilter.LINK_URL:
+ if (null != filterClass && !filterClass.equals(LinkNameFilter.class)) {
+ throw new JsonParseException(parser, PARSE_ERROR_MESSAGE_AMBIGUOUS_FILTER, parser.getCurrentLocation());
}
filterClass = LinkNameFilter.class;
break;
@@ -116,12 +117,12 @@ class QuickLinksFilterDeserializer extends StdDeserializer<Filter> {
}
}
if (!invalidAttributes.isEmpty()) {
- throw new JsonParseException(PARSE_ERROR_MESSAGE_INVALID_JSON_TAG + invalidAttributes,
+ throw new JsonParseException(parser, PARSE_ERROR_MESSAGE_INVALID_JSON_TAG + invalidAttributes,
parser.getCurrentLocation());
}
if (null == filterClass) {
filterClass = AcceptAllFilter.class;
}
- return mapper.readValue(root, filterClass);
+ return mapper.readValue(root.traverse(), filterClass);
}
}
\ No newline at end of file
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
index 07cce29..b3ef612 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
@@ -18,18 +18,19 @@
package org.apache.ambari.server.state.quicklinksprofile;
-import java.util.List;
+import static java.util.Collections.emptyList;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.map.annotate.JsonSerialize;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
/**
* Class to represent component-level filter definitions
*/
-@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Service {
@JsonProperty("name")
@@ -62,7 +63,7 @@ public class Service {
* @return component-specific quicklink filter definitions for components of this service
*/
public List<Component> getComponents() {
- return components;
+ return null != components ? components : emptyList();
}
public void setComponents(List<Component> components) {
@@ -73,7 +74,7 @@ public class Service {
* @return service-specific filters for this service
*/
public List<Filter> getFilters() {
- return filters;
+ return null != filters ? filters : emptyList();
}
public void setFilters(List<Filter> filters) {
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
index 286a10c..250e816 100644
--- 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
@@ -19,6 +19,8 @@
package org.apache.ambari.server.state.quicklinksprofile;
+import java.util.Optional;
+
import javax.annotation.Nonnull;
import org.apache.ambari.server.state.quicklinks.Link;
@@ -35,4 +37,8 @@ public class ShowAllLinksVisibilityController implements QuickLinkVisibilityCont
return true;
}
+ @Override
+ public Optional<String> getUrlOverride(@Nonnull String service, @Nonnull Link quickLink) {
+ return Optional.empty();
+ }
}
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/utils/StreamUtils.java
similarity index 59%
copy from ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java
copy to ambari-server/src/main/java/org/apache/ambari/server/utils/StreamUtils.java
index caa2e2e..5a846ed 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinkVisibilityController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/utils/StreamUtils.java
@@ -16,22 +16,24 @@
* limitations under the License.
*/
-package org.apache.ambari.server.state.quicklinksprofile;
+package org.apache.ambari.server.utils;
+import java.util.stream.Stream;
-import javax.annotation.Nonnull;
-
-import org.apache.ambari.server.state.quicklinks.Link;
-
-
-public interface QuickLinkVisibilityController {
+/**
+ * Utilities for Streams
+ */
+public class StreamUtils {
/**
- * @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
+ * Filters a stream for instances of a class and returns a typed stream
+ * @param stream the stream to filter
+ * @param clazz stream will be filtered to instances of this class
+ * @param <T> the type of the class
+ * @return A stream of containing only instances of {@link T}
*/
- boolean isVisible(@Nonnull String service, @Nonnull Link quickLink);
+ public static <T> Stream<T> instancesOf(Stream<?> stream, Class<? extends T> clazz) {
+ return stream.filter(clazz::isInstance).map(clazz::cast);
+ }
}
-
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
index 5ed582f..ecd6a5a 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
@@ -426,7 +426,7 @@ public class ProvisionClusterRequestTest {
ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
assertEquals("Quick links profile doesn't match expected",
- "{\"filters\":[{\"visible\":true}],\"services\":[]}",
+ "{\"filters\":[{\"visible\":true}]}",
request.getQuickLinksProfileJson());
}
@@ -441,7 +441,7 @@ public class ProvisionClusterRequestTest {
ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
assertEquals("Quick links profile doesn't match expected",
- "{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+ "{\"services\":[{\"name\":\"HDFS\",\"filters\":[{\"visible\":true}]}]}",
request.getQuickLinksProfileJson());
}
@@ -460,7 +460,7 @@ public class ProvisionClusterRequestTest {
ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
System.out.println(request.getQuickLinksProfileJson());
assertEquals("Quick links profile doesn't match expected",
- "{\"filters\":[{\"visible\":true}],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+ "{\"filters\":[{\"visible\":true}],\"services\":[{\"name\":\"HDFS\",\"filters\":[{\"visible\":true}]}]}",
request.getQuickLinksProfileJson());
}
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
index 9ce6471..a2cb231 100644
--- 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
@@ -21,6 +21,7 @@ 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.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
@@ -41,9 +42,7 @@ 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;
@@ -97,6 +96,28 @@ public class QuickLinkArtifactResourceProviderTest {
}
/**
+ * Test the application of link url override in the quick links profile
+ */
+ @Test
+ public void getResourcesWithUrlOverride() 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.newHashSet()), predicate);
+ Map<String, Link> linkMap = getLinks(resources);
+
+ assertEquals("http://customlink.org/resourcemanager", linkMap.get("resourcemanager_ui").getUrl());
+ }
+
+ /**
* Test to prove the all links are visible if no profile is set
*/
@Test
@@ -171,14 +192,8 @@ public class QuickLinkArtifactResourceProviderTest {
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();
+ expect(amc.getQuicklinkVisibilityController())
+ .andAnswer(() -> QuickLinkVisibilityControllerFactory.get(quicklinkProfile)).anyTimes();
try {
expect(metaInfo.getStack(anyString(), anyString())).andReturn(stack).anyTimes();
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
index 5821006..d69a167 100644
--- 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
@@ -25,11 +25,11 @@ import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
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;
@@ -62,14 +62,14 @@ public class FilterEvaluatorTest {
@Test
public void testWithEmptyFilters() throws Exception {
FilterEvaluator evaluator = new FilterEvaluator(new ArrayList<>());
- assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
+ assertEquals(Optional.empty(), evaluator.isVisible(namenodeUi));
FilterEvaluator evaluator2 = new FilterEvaluator(null);
- assertEquals(Optional.absent(), evaluator2.isVisible(namenodeUi));
+ assertEquals(Optional.empty(), evaluator2.isVisible(namenodeUi));
}
/**
- * FilterEvaluator should return {@link Optional#absent()} when the link doesn't match any filters
+ * FilterEvaluator should return {@link Optional.empty()} when the link doesn't match any filters
*/
@Test
public void testNoMatchingFilter() throws Exception {
@@ -77,7 +77,7 @@ public class FilterEvaluatorTest {
linkNameFilter(NAMENODE_JMX, true),
linkAttributeFilter(SSO, false));
FilterEvaluator evaluator = new FilterEvaluator(filters);
- assertEquals(Optional.absent(), evaluator.isVisible(namenodeUi));
+ assertEquals(Optional.empty(), evaluator.isVisible(namenodeUi));
}
/**
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
index 1a36b9d..aa5a2df 100644
--- 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
@@ -18,9 +18,13 @@
package org.apache.ambari.server.state.quicklinksprofile;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.util.List;
+import java.util.Optional;
+
import org.apache.ambari.server.state.quicklinks.Link;
import org.junit.Test;
@@ -32,16 +36,26 @@ public class QuickLinkVisibilityControllerTest {
static final String SSO = "sso";
static final String NAMENODE = "NAMENODE";
static final String HDFS = "HDFS";
+ public static final String YARN = "YARN";
static final String NAMENODE_UI = "namenode_ui";
+ static final String NAMENODE_LOGS = "namenode_logs";
+ static final String NAMENODE_JMX = "namenode_jmx";
+ static final String THREAD_STACKS = "Thread Stacks";
+ static final String LINK_URL_1 = "www.overridden.org/1";
+ static final String LINK_URL_2 = "www.overridden.org/2";
+ static final String LINK_URL_3 = "www.overridden.org/3";
private Link namenodeUi;
+ private Link namenodeLogs;
+ private Link namenodeJmx;
+ private Link threadStacks;
public QuickLinkVisibilityControllerTest() {
- namenodeUi = new Link();
- namenodeUi.setComponentName(NAMENODE);
- namenodeUi.setName(NAMENODE_UI);
- namenodeUi.setAttributes(ImmutableList.of(AUTHENTICATED));
+ namenodeUi = link(NAMENODE_UI, NAMENODE, ImmutableList.of(AUTHENTICATED));
+ namenodeLogs = link(NAMENODE_LOGS, NAMENODE, null);
+ namenodeJmx = link(NAMENODE_JMX, NAMENODE, null);
+ threadStacks = link(THREAD_STACKS, NAMENODE, null);
}
/**
@@ -178,4 +192,65 @@ public class QuickLinkVisibilityControllerTest {
evaluator.isVisible(HDFS, namenodeUi));
}
+ @Test
+ public void testUrlOverride() throws Exception {
+ Component nameNode = Component.create(
+ NAMENODE,
+ ImmutableList.of(
+ Filter.linkNameFilter(NAMENODE_UI, true),
+ Filter.linkNameFilter(NAMENODE_LOGS, LINK_URL_1, true)));
+ Service hdfs = Service.create(
+ HDFS,
+ ImmutableList.of(Filter.linkNameFilter(NAMENODE_JMX, LINK_URL_2, true)),
+ ImmutableList.of(nameNode));
+ QuickLinksProfile profile = QuickLinksProfile.create(
+ ImmutableList.of(Filter.linkNameFilter(THREAD_STACKS, LINK_URL_3, true)),
+ ImmutableList.of(hdfs));
+
+ DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+ assertEquals(Optional.empty(), evaluator.getUrlOverride(HDFS, namenodeUi));
+ assertEquals(Optional.of(LINK_URL_1), evaluator.getUrlOverride(HDFS, namenodeLogs));
+ assertEquals(Optional.of(LINK_URL_2), evaluator.getUrlOverride(HDFS, namenodeJmx));
+ // component name doesn't matter
+ namenodeLogs.setComponentName(null);
+ assertEquals(Optional.of(LINK_URL_1), evaluator.getUrlOverride(HDFS, namenodeLogs));
+ // no override for links not in the profile
+ assertEquals(Optional.empty(), evaluator.getUrlOverride(YARN, link("resourcemanager_ui", "RESOURCEMANAGER", null)));
+ // url overrides in global filters are ignored
+ assertEquals(Optional.empty(), evaluator.getUrlOverride(HDFS, threadStacks));
+ }
+
+ @Test
+ public void testUrlOverride_duplicateDefinitions() throws Exception {
+ // same link is defined twice for a service
+ Component nameNode = Component.create(
+ NAMENODE,
+ ImmutableList.of(
+ Filter.linkNameFilter(NAMENODE_UI, LINK_URL_1, true))); // this will override service level setting for the same link
+ Service hdfs = Service.create(
+ HDFS,
+ ImmutableList.of(Filter.linkNameFilter(NAMENODE_UI, LINK_URL_2, true)), // same link on service level with different url
+ ImmutableList.of(nameNode));
+ Service yarn = Service.create(
+ YARN,
+ ImmutableList.of(Filter.linkNameFilter(NAMENODE_UI, LINK_URL_3, true)), // this belongs to an other service so doesn't affect outcome
+ ImmutableList.of(nameNode));
+
+ QuickLinksProfile profile = QuickLinksProfile.create(
+ ImmutableList.of(),
+ ImmutableList.of(hdfs));
+
+ DefaultQuickLinkVisibilityController evaluator = new DefaultQuickLinkVisibilityController(profile);
+ assertEquals(Optional.of(LINK_URL_1), evaluator.getUrlOverride(HDFS, namenodeUi));
+ }
+
+
+ private static final Link link(String name, String componentName, List<String> attributes) {
+ Link link = new Link();
+ link.setName(name);
+ link.setComponentName(componentName);
+ link.setAttributes(attributes);
+ return link;
+ }
+
}
\ No newline at end of file
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
index 49244d4..9dba51d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
@@ -79,8 +79,13 @@ public class QuickLinksProfileBuilderTest {
public void testBuildProfileBothGlobalAndServiceFilters() throws Exception {
Set<Map<String, String>> globalFilters = newHashSet( filter(null, null, false) );
- Map<String, Object> nameNode = component("NAMENODE",
- newHashSet(filter("namenode_ui", null, false)));
+ Map<String, Object> nameNode = component(
+ "NAMENODE",
+ newHashSet(
+ filter("namenode_ui", null, false),
+ filter("namenode_logs", null, "http://customlink.org/namenode_logs", true)
+ )
+ );
Map<String, Object> hdfs = service("HDFS",
newHashSet(nameNode),
@@ -94,6 +99,9 @@ public class QuickLinksProfileBuilderTest {
QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
assertFilterExists(profile, null, null, Filter.acceptAllFilter(false));
assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+ assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+ assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_logs",
+ "http://customlink.org/namenode_logs", true));
assertFilterExists(profile, "HDFS", null, Filter.linkAttributeFilter("sso", true));
}
@@ -208,11 +216,23 @@ public class QuickLinksProfileBuilderTest {
throw new AssertionError("Expected service not found: " + serviceName);
}
- public static Map<String, String> filter(@Nullable String linkName, @Nullable String attributeName, boolean visible) {
- Map<String, String> map = new HashMap<>(3);
+ public static Map<String, String> filter(@Nullable String linkName,
+ @Nullable String attributeName,
+ boolean visible) {
+ return filter(linkName, attributeName, null, visible);
+ }
+
+ public static Map<String, String> filter(@Nullable String linkName,
+ @Nullable String attributeName,
+ @Nullable String linkUrl,
+ boolean visible) {
+ Map<String, String> map = new HashMap<>(4);
if (null != linkName) {
map.put(LinkNameFilter.LINK_NAME, linkName);
}
+ if (null != linkUrl) {
+ map.put(LinkNameFilter.LINK_URL, linkUrl);
+ }
if (null != attributeName) {
map.put(LinkAttributeFilter.LINK_ATTRIBUTE, attributeName);
}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
index 8b01ca5..abe55bf 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParserTest.java
@@ -20,9 +20,9 @@ package org.apache.ambari.server.state.quicklinksprofile;
import static org.junit.Assert.assertEquals;
-import org.codehaus.jackson.JsonParseException;
import org.junit.Test;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.io.Resources;
public class QuickLinksProfileParserTest {
@@ -50,7 +50,7 @@ public class QuickLinksProfileParserTest {
Component nameNode = hdfs.getComponents().get(0);
assertEquals(2, nameNode.getFilters().size());
assertEquals(
- Filter.linkNameFilter("namenode_ui", false),
+ Filter.linkNameFilter("namenode_ui", "http://customlink.org/namenode", false),
nameNode.getFilters().get(0));
Component historyServer = profile.getServices().get(1).getComponents().get(0);
@@ -62,18 +62,25 @@ public class QuickLinksProfileParserTest {
Service yarn = profile.getServices().get(2);
assertEquals(1, yarn.getFilters().size());
assertEquals(
- Filter.linkNameFilter("resourcemanager_ui", true),
+ Filter.linkNameFilter("resourcemanager_ui", "http://customlink.org/resourcemanager", true),
yarn.getFilters().get(0));
}
- @Test(expected = JsonParseException.class)
+ @Test(expected = JsonProcessingException.class)
public void testParseInconsistentProfile_ambigousFilterDefinition() throws Exception {
String profileName = "inconsistent_quicklinks_profile.json";
QuickLinksProfileParser parser = new QuickLinksProfileParser();
parser.parse(Resources.getResource(profileName));
}
- @Test(expected = JsonParseException.class)
+ @Test(expected = JsonProcessingException.class)
+ public void testParseInconsistentProfile_invalidLinkUrl() throws Exception {
+ String profileName = "inconsistent_quicklinks_profile_4.json";
+ QuickLinksProfileParser parser = new QuickLinksProfileParser();
+ parser.parse(Resources.getResource(profileName));
+ }
+
+ @Test(expected = JsonProcessingException.class)
public void testParseInconsistentProfile_misspelledFilerDefinition() throws Exception {
String profileName = "inconsistent_quicklinks_profile_3.json";
QuickLinksProfileParser parser = new QuickLinksProfileParser();
diff --git a/ambari-server/src/test/resources/example_quicklinks_profile.json b/ambari-server/src/test/resources/example_quicklinks_profile.json
index 50ea5e8..227761f 100644
--- a/ambari-server/src/test/resources/example_quicklinks_profile.json
+++ b/ambari-server/src/test/resources/example_quicklinks_profile.json
@@ -20,6 +20,7 @@
"filters": [
{
"link_name": "namenode_ui",
+ "link_url" : "http://customlink.org/namenode",
"visible": false
},
{
@@ -49,6 +50,7 @@
"filters": [
{
"link_name": "resourcemanager_ui",
+ "link_url" : "http://customlink.org/resourcemanager",
"visible": true
}
]
diff --git a/ambari-server/src/test/resources/inconsistent_quicklinks_profile_4.json b/ambari-server/src/test/resources/inconsistent_quicklinks_profile_4.json
new file mode 100644
index 0000000..7bc04ed
--- /dev/null
+++ b/ambari-server/src/test/resources/inconsistent_quicklinks_profile_4.json
@@ -0,0 +1,10 @@
+{
+ "filters": [
+ {
+ "link_attributes": "sso",
+ "link_url": "http://quicklinks.org/link",
+ "visible": true
+ }
+ ],
+ "services": []
+}
\ No newline at end of file