Skip to content

Commit

Permalink
KNOX-3003 - Services with more than one serviceUrl metadata are group…
Browse files Browse the repository at this point in the history
…ed on the Knox Home page
  • Loading branch information
smolnar82 committed Jan 31, 2024
1 parent b855e0f commit 9b470dd
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,8 @@ private TopologyInformationWrapper getTopologies(String topologyName) {
Set<ServiceModel> apiServices = new HashSet<>();
Set<ServiceModel> uiServices = new HashSet<>();
topology.getServices().stream().filter(service -> !UNREAL_SERVICES.contains(service.getRole())).forEach(service -> {
service.getUrls().forEach(serviceUrl -> {
ServiceModel serviceModel = getServiceModel(request, config.getGatewayPath(), topology.getName(), service, getServiceMetadata(serviceDefinitionRegistry, service),
serviceUrl);
if (!service.getUrls().isEmpty()) {
final ServiceModel serviceModel = getServiceModel(request, config.getGatewayPath(), topology.getName(), service, getServiceMetadata(serviceDefinitionRegistry, service));
if (ServiceModel.Type.UI == serviceModel.getType()) {
uiServices.add(serviceModel);
} else if (ServiceModel.Type.API_AND_UI == serviceModel.getType()) {
Expand All @@ -240,7 +239,7 @@ private TopologyInformationWrapper getTopologies(String topologyName) {
} else {
apiServices.add(serviceModel);
}
});
}
});
topologies.addTopology(topology.getName(), isPinnedTopology(topology.getName(), config), config.getApiServicesViewVersionOnHomepage(), new TreeSet<>(apiServices), new TreeSet<>(uiServices));
}
Expand All @@ -264,14 +263,13 @@ private Metadata getServiceMetadata(ServiceDefinitionRegistry serviceDefinitionR
return serviceDefinition.isPresent() ? serviceDefinition.get().getService().getMetadata() : null;
}

private ServiceModel getServiceModel(HttpServletRequest request, String gatewayPath, String topologyName, Service service, Metadata serviceMetadata, String serviceUrl) {
private ServiceModel getServiceModel(HttpServletRequest request, String gatewayPath, String topologyName, Service service, Metadata serviceMetadata) {
final ServiceModel serviceModel = new ServiceModel();
serviceModel.setRequest(request);
serviceModel.setGatewayPath(gatewayPath);
serviceModel.setTopologyName(topologyName);
serviceModel.setService(service);
serviceModel.setServiceMetadata(serviceMetadata);
serviceModel.setServiceUrl(serviceUrl);
return serviceModel;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@
*/
package org.apache.knox.gateway.service.metadata;

import static java.lang.String.format;
import static java.util.Locale.ROOT;

import java.io.UncheckedIOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.annotation.XmlAccessType;
Expand Down Expand Up @@ -60,7 +66,6 @@ public enum Type {
private String gatewayPath;
private Service service;
private Metadata serviceMetadata;
private String serviceUrl;

public void setRequest(HttpServletRequest request) {
this.request = request;
Expand All @@ -82,10 +87,6 @@ public void setServiceMetadata(Metadata serviceMetadata) {
this.serviceMetadata = serviceMetadata;
}

public void setServiceUrl(String serviceUrl) {
this.serviceUrl = serviceUrl;
}

@XmlElement
public String getServiceName() {
return this.service == null ? "" : service.getRole();
Expand Down Expand Up @@ -124,48 +125,60 @@ public String getContext() {
return serviceMetadata == null ? ("/" + getServiceName().toLowerCase(Locale.ROOT)) : serviceMetadata.getContext();
}

@XmlElement
public String getServiceUrl() {
String context = getContext();
@XmlElement(name = "serviceUrls")
public List<String> getServiceUrls() {
final Set<String> resolvedServiceUrls = new TreeSet<>();
final String context = getContext();

if (HIVE_SERVICE_NAME.equals(getServiceName())) {
return String.format(Locale.ROOT, HIVE_SERVICE_URL_TEMPLATE, request.getServerName(), request.getServerPort(), gatewayPath, topologyName);
resolvedServiceUrls.add(format(ROOT, HIVE_SERVICE_URL_TEMPLATE, request.getServerName(), request.getServerPort(), gatewayPath, topologyName));
} else if (IMPALA_SERVICE_NAME.equals(getServiceName())) {
return String.format(Locale.ROOT, IMPALA_SERVICE_URL_TEMPLATE, request.getServerName(), request.getServerPort(), gatewayPath, topologyName);
resolvedServiceUrls.add(format(ROOT, IMPALA_SERVICE_URL_TEMPLATE, request.getServerName(), request.getServerPort(), gatewayPath, topologyName));
} else {
return getServiceUrl(context);
if (service != null && service.getUrls() != null && !service.getUrls().isEmpty()) {
this.service.getUrls().forEach(serviceUrl -> {
resolvedServiceUrls.add(getServiceUrl(context, serviceUrl));
});
} else {
// fall back to the service URL fetched from the 'service' instance, if any
resolvedServiceUrls.add(getServiceUrl(context, null));
}
}
return Arrays.asList(resolvedServiceUrls.toArray(new String[0]));
}

private String getServiceUrl(String context) {
final String resolvedContext = resolvePlaceholdersFromBackendUrl(context);
private String getServiceUrl(String context, String serviceUrl) {
final String resolvedContext = resolvePlaceholdersFromBackendUrl(context, serviceUrl);
return String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, request.getScheme(), request.getServerName(), request.getServerPort(), gatewayPath, topologyName, resolvedContext);
}

private String resolvePlaceholdersFromBackendUrl(String resolveable) {
private String resolvePlaceholdersFromBackendUrl(String resolveable, String serviceUrl) {
String toBeResolved = resolveable;
if (toBeResolved != null) {
final String backendUrlString = getBackendServiceUrl();
final String backendUrlString = getBackendServiceUrl(serviceUrl);

if (toBeResolved.indexOf("{{BACKEND_HOST}}") > -1) {
toBeResolved = toBeResolved.replace("{{BACKEND_HOST}}", backendUrlString);
}
if (StringUtils.isNotBlank(backendUrlString)) {
if (toBeResolved.indexOf("{{BACKEND_HOST}}") > -1) {
toBeResolved = toBeResolved.replace("{{BACKEND_HOST}}", backendUrlString);
}

if (toBeResolved.indexOf("{{SCHEME}}") > -1 || toBeResolved.indexOf("{{HOST}}") > -1 || toBeResolved.indexOf("{{PORT}}") > -1) {
try {
final URL backendUrl = new URL(backendUrlString);
toBeResolved = toBeResolved.replace("{{SCHEME}}", backendUrl.getProtocol());
toBeResolved = toBeResolved.replace("{{HOST}}", backendUrl.getHost());
toBeResolved = toBeResolved.replace("{{PORT}}", String.valueOf(backendUrl.getPort()));
} catch (MalformedURLException e) {
throw new UncheckedIOException("Error while converting " + backendUrlString + " to a URL", e);
if (toBeResolved.indexOf("{{SCHEME}}") > -1 || toBeResolved.indexOf("{{HOST}}") > -1 || toBeResolved.indexOf("{{PORT}}") > -1) {
try {
final URL backendUrl = new URL(backendUrlString);
toBeResolved = toBeResolved.replace("{{SCHEME}}", backendUrl.getProtocol());
toBeResolved = toBeResolved.replace("{{HOST}}", backendUrl.getHost());
toBeResolved = toBeResolved.replace("{{PORT}}", String.valueOf(backendUrl.getPort()));
} catch (MalformedURLException e) {
throw new UncheckedIOException("Error while converting '" + backendUrlString + "' to a URL", e);
}
}
}
}

return toBeResolved;
}

String getBackendServiceUrl() {
String getBackendServiceUrl(String serviceUrl) {
final String backendServiceUrl = serviceUrl == null ? (service == null ? "" : service.getUrl()) : serviceUrl;
return backendServiceUrl == null ? "" : backendServiceUrl;
}
Expand All @@ -183,7 +196,9 @@ public List<Sample> getSamples() {
} else {
final String method = StringUtils.isBlank(sample.getMethod()) ? "GET" : sample.getMethod();
final String path = sample.getPath().startsWith("/") ? sample.getPath() : ("/" + sample.getPath());
resolvedSample.setValue(String.format(Locale.ROOT, CURL_SAMPLE_TEMPLATE, method, getServiceUrl(), path));
final String serviceUrl = getServiceUrls().isEmpty() ? (service == null ? "$SERVICE_URL" : service.getUrl())
: getServiceUrls().stream().findFirst().get();
resolvedSample.setValue(String.format(Locale.ROOT, CURL_SAMPLE_TEMPLATE, method, serviceUrl, path));
}
samples.add(resolvedSample);
});
Expand All @@ -201,27 +216,27 @@ public boolean equals(Object obj) {
}
final ServiceModel serviceModel = (ServiceModel) obj;
return new EqualsBuilder().append(topologyName, serviceModel.topologyName).append(gatewayPath, serviceModel.gatewayPath).append(getServiceName(), serviceModel.getServiceName())
.append(getVersion(), serviceModel.getVersion()).append(serviceMetadata, serviceModel.serviceMetadata).append(getServiceUrl(), serviceModel.getServiceUrl()).isEquals();
.append(getVersion(), serviceModel.getVersion()).append(serviceMetadata, serviceModel.serviceMetadata).append(getServiceUrls(), serviceModel.getServiceUrls()).isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(topologyName).append(gatewayPath).append(getServiceName()).append(getVersion()).append(serviceMetadata).append(getServiceUrl())
return new HashCodeBuilder(17, 37).append(topologyName).append(gatewayPath).append(getServiceName()).append(getVersion()).append(serviceMetadata).append(getServiceUrls())
.toHashCode();
}

@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(topologyName).append(gatewayPath).append(getServiceName()).append(getVersion())
.append(serviceMetadata).append(getServiceUrl()).toString();
.append(serviceMetadata).append(getServiceUrls()).toString();
}

@Override
public int compareTo(ServiceModel other) {
final int byServiceName = getServiceName().compareTo(other.getServiceName());
if (byServiceName == 0) {
final int byVersion = getVersion().compareTo(getVersion());
return byVersion == 0 ? getBackendServiceUrl().compareTo(other.getBackendServiceUrl()) : byVersion;
return byVersion == 0 ? Integer.compare(getServiceUrls().size(), other.getServiceUrls().size()) : byVersion;
}
return byServiceName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public void shouldReturnProperHiveServiceUrl() throws Exception {
serviceModel.setService(service);
EasyMock.expect(service.getRole()).andReturn(HIVE_SERVICE_NAME).anyTimes();
EasyMock.replay(service);
assertEquals(String.format(Locale.ROOT, HIVE_SERVICE_URL_TEMPLATE, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName), serviceModel.getServiceUrl());
assertEquals(String.format(Locale.ROOT, HIVE_SERVICE_URL_TEMPLATE, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName), getFirstServiceUrl(serviceModel));
}

@Test
Expand All @@ -173,7 +173,7 @@ public void shouldReturnProperImpalaServiceUrl() throws Exception {
serviceModel.setService(service);
EasyMock.expect(service.getRole()).andReturn(IMPALA_SERVICE_NAME).anyTimes();
EasyMock.replay(service);
assertEquals(String.format(Locale.ROOT, IMPALA_SERVICE_URL_TEMPLATE, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName), serviceModel.getServiceUrl());
assertEquals(String.format(Locale.ROOT, IMPALA_SERVICE_URL_TEMPLATE, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName), getFirstServiceUrl(serviceModel));
}

public HttpServletRequest setUpHttpRequestMock() {
Expand All @@ -198,7 +198,7 @@ public void shouldReturnSimpleServiceUrl() throws Exception {
final String context = "/testContext";
EasyMock.expect(metadata.getContext()).andReturn(context).anyTimes();
EasyMock.replay(metadata);
assertEquals(String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, SERVER_SCHEME, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName, context), serviceModel.getServiceUrl());
assertEquals(String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, SERVER_SCHEME, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName, context), getFirstServiceUrl(serviceModel));
}

@Test
Expand All @@ -214,20 +214,24 @@ public void shouldReturnServiceUrlWithBackendHostPlaceholder() throws Exception
serviceModel.setService(service);
EasyMock.expect(service.getRole()).andReturn("service").anyTimes();
final String backendHost = "https://localhost:5555";
EasyMock.expect(service.getUrl()).andReturn(backendHost); // backend host comes from the service object
EasyMock.expect(service.getUrl()).andReturn(backendHost).anyTimes(); // backend host comes from the service object
final Metadata metadata = EasyMock.createNiceMock(Metadata.class);
serviceModel.setServiceMetadata(metadata);
final String context = "/testContext?backendHost={{BACKEND_HOST}}";
EasyMock.expect(metadata.getContext()).andReturn(context).anyTimes();
EasyMock.replay(service, metadata);
assertEquals(String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, SERVER_SCHEME, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName,
context.replace("{{BACKEND_HOST}}", backendHost)), serviceModel.getServiceUrl());
context.replace("{{BACKEND_HOST}}", backendHost)), getFirstServiceUrl(serviceModel));

final String serviceUrl = "https://serviceHost:8888";
serviceModel.setServiceUrl(serviceUrl); // backend host comes from the given service URL
final Service serviceWithServiceUrls = EasyMock.createNiceMock(Service.class);
// backend host comes from the given service URLs
EasyMock.expect(serviceWithServiceUrls.getUrls()).andReturn(Arrays.asList(serviceUrl)).anyTimes();
EasyMock.replay(serviceWithServiceUrls);
serviceModel.setService(serviceWithServiceUrls);
assertEquals(
String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, SERVER_SCHEME, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName, context.replace("{{BACKEND_HOST}}", serviceUrl)),
serviceModel.getServiceUrl());
getFirstServiceUrl(serviceModel));
}

@Test
Expand All @@ -242,7 +246,7 @@ public void shouldReturnServiceUrlWithSchemeHostAndPortPlaceholders() throws Exc
final Service service = EasyMock.createNiceMock(Service.class);
serviceModel.setService(service);
EasyMock.expect(service.getRole()).andReturn("service").anyTimes();
EasyMock.expect(service.getUrl()).andReturn("https://localhost:5555");
EasyMock.expect(service.getUrl()).andReturn("https://localhost:5555").anyTimes();

final Metadata metadata = EasyMock.createNiceMock(Metadata.class);
serviceModel.setServiceMetadata(metadata);
Expand All @@ -251,7 +255,7 @@ public void shouldReturnServiceUrlWithSchemeHostAndPortPlaceholders() throws Exc

EasyMock.replay(service, metadata);
assertEquals(String.format(Locale.ROOT, SERVICE_URL_TEMPLATE, SERVER_SCHEME, SERVER_NAME, SERVER_PORT, gatewayPath, topologyName,
context.replace("{{SCHEME}}", "https").replace("{{HOST}}", "localhost").replace("{{PORT}}", "5555")), serviceModel.getServiceUrl());
context.replace("{{SCHEME}}", "https").replace("{{HOST}}", "localhost").replace("{{PORT}}", "5555")), getFirstServiceUrl(serviceModel));
}

@Test
Expand Down Expand Up @@ -285,4 +289,8 @@ public void shouldReturnProperSampleValueEvenIfPathDoesNotStartWithSlash() throw
assertEquals(samples.get(0).getValue(), "curl -iv -X PUT \"https://localhost:8443/gateway/sandbox/testContext/sampleStartsWithSlashPath/operation\"");
assertEquals(samples.get(1).getValue(), "curl -iv -X GET \"https://localhost:8443/gateway/sandbox/testContext/sampleStartsWithoutSlashPath/operation\"");
}

private String getFirstServiceUrl(ServiceModel serviceModel) {
return serviceModel.getServiceUrls().isEmpty() ? "" : serviceModel.getServiceUrls().get(0);
}
}
2 changes: 1 addition & 1 deletion knox-homepage-ui/home/app/topologies/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Service {
description: string;
serviceName: string;
version: string;
serviceUrl: string;
serviceUrls: string[];
shortDesc: string;
type: string;
context: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ mat-grid-tile-footer figcaption h3 {
transition: all 0.3s linear;
visibility: visible;
}

/deep/ .groupServiceInformationModal {
width: 80%;
}
Loading

0 comments on commit 9b470dd

Please sign in to comment.