Added support for multiple apps in request mapping live hovers

This commit is contained in:
nsingh
2017-10-25 13:15:18 -07:00
parent 23d55a6d93
commit 3b933ea61f
2 changed files with 113 additions and 28 deletions

View File

@@ -58,7 +58,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
if (runningApps.length > 0) {
Optional<RequestMappingMethod> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
Optional<List<RequestMappingMethod>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(hoverRange);
@@ -77,7 +77,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
try {
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
Optional<RequestMappingMethod> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
Optional<List<RequestMappingMethod>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
addHoverContent(val.get(), hoverContent);
@@ -97,13 +97,14 @@ public class RequestMappingHoverProvider implements HoverProvider {
return null;
}
private Optional<RequestMappingMethod> getRequestMappingMethodFromRunningApp(Annotation annotation,
private Optional<List<RequestMappingMethod>> getRequestMappingMethodFromRunningApp(Annotation annotation,
SpringBootApp[] runningApps) {
try {
List<RequestMappingMethod> methods = new ArrayList<>();
for (SpringBootApp app : runningApps) {
String mappings = app.getRequestMappings();
if (mappings!=null) {
if (mappings != null) {
JSONObject requestMappings = new JSONObject(mappings);
String rawPath = getRawPath(annotation, requestMappings);
if (rawPath != null) {
@@ -112,12 +113,16 @@ public class RequestMappingHoverProvider implements HoverProvider {
String rawMethod = getRawMethod(annotation, requestMappings);
JLRMethod parsedMethod = JLRMethodParser.parse(rawMethod);
if (methodMatchesAnnotation(annotation, parsedMethod)) {
return Optional.of(new RequestMappingMethod(path, parsedMethod, app));
methods.add(new RequestMappingMethod(path, parsedMethod, app));
}
}
}
}
}
if (!methods.isEmpty()) {
return Optional.of(methods);
}
} catch (Exception e) {
Log.log(e);
}
@@ -141,28 +146,37 @@ public class RequestMappingHoverProvider implements HoverProvider {
return false;
}
private void addHoverContent(RequestMappingMethod mappingMethod, List<Either<String, MarkedString>> hoverContent) throws Exception {
String processId = mappingMethod.app.getProcessID();
String processName = mappingMethod.app.getProcessName();
String path = mappingMethod.requestMappingPath;
private void addHoverContent(List<RequestMappingMethod> mappingMethods, List<Either<String, MarkedString>> hoverContent)
throws Exception {
for (int i = 0; i < mappingMethods.size() ; i++) {
RequestMappingMethod method = mappingMethods.get(i);
String processId = method.app.getProcessID();
String processName = method.app.getProcessName();
String path = method.requestMappingPath;
StringBuilder builder = new StringBuilder();
StringBuilder builder = new StringBuilder();
String port = mappingMethod.app.getPort();
String host = mappingMethod.app.getHost();
String url = UrlUtil.createUrl(host, port, path);
String port = method.app.getPort();
String host = method.app.getHost();
String url = UrlUtil.createUrl(host, port, path);
builder.append("Path: ");
builder.append("[");
builder.append(path);
builder.append("]");
builder.append("(");
builder.append(url);
builder.append(")");
builder.append("Path: ");
builder.append("[");
builder.append(path);
builder.append("]");
builder.append("(");
builder.append(url);
builder.append(")");
hoverContent.add(Either.forLeft(builder.toString()));
hoverContent.add(Either.forLeft("Process ID: " + processId));
hoverContent.add(Either.forLeft("Process Name: " + processName));
if (i < mappingMethods.size() - 1) {
// Three dashes == line separator in Markdown
hoverContent.add(Either.forLeft("---"));
}
}
hoverContent.add(Either.forLeft(builder.toString()));
hoverContent.add(Either.forLeft("Process ID: " + processId));
hoverContent.add(Either.forLeft("Process Name: " + processName));
}
private String getRawMethod(Annotation annotation, JSONObject mappings) {
@@ -170,7 +184,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
while (keys.hasNext()) {
String key = keys.next();
if (matchesAnnotation(annotation, key)) {
Object ob= mappings.get(key);
Object ob = mappings.get(key);
if (ob instanceof JSONObject) {
JSONObject methodMap = (JSONObject) ob;
return methodMap.getString("method");
@@ -196,18 +210,18 @@ public class RequestMappingHoverProvider implements HoverProvider {
if (annotation instanceof SingleMemberAnnotation) {
Expression valueContent = ((SingleMemberAnnotation) annotation).getValue();
if (valueContent instanceof StringLiteral) {
mappingPath = ((StringLiteral)valueContent).getLiteralValue();
mappingPath = ((StringLiteral) valueContent).getLiteralValue();
}
}
else if (annotation instanceof NormalAnnotation) {
List<?> values = ((NormalAnnotation) annotation).values();
for (Object value : values) {
if (value instanceof MemberValuePair) {
String name = ((MemberValuePair)value).getName().toString();
String name = ((MemberValuePair) value).getName().toString();
if (name != null && name.equals("value")) {
Expression valueContent = ((MemberValuePair)value).getValue();
Expression valueContent = ((MemberValuePair) value).getValue();
if (valueContent instanceof StringLiteral) {
mappingPath = ((StringLiteral)valueContent).getLiteralValue();
mappingPath = ((StringLiteral) valueContent).getLiteralValue();
}
}
}

View File

@@ -132,4 +132,75 @@ public class RequestMappingLiveHoverTest {
}
@Test
public void testMultipleAppsLiveHover() throws Exception {
File directory = new File(
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java";
// Build three different instances of the same app running on different ports with different process IDs
mockAppProvider.builder()
.isSpringBootApp(true)
.port("1000")
.processId("70000")
.host("cfapps.io")
.processName("test-request-mapping-live-hover")
// Ugly, but this is real JSON copied from a real live running app. We want the
// mock app to return realistic results if possible
.getRequestMappings(
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
. build();
mockAppProvider.builder()
.isSpringBootApp(true)
.port("1001")
.processId("80000")
.host("cfapps.io")
.processName("test-request-mapping-live-hover")
// Ugly, but this is real JSON copied from a real live running app. We want the
// mock app to return realistic results if possible
.getRequestMappings(
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
. build();
mockAppProvider.builder()
.isSpringBootApp(true)
.port("1002")
.processId("90000")
.host("cfapps.io")
.processName("test-request-mapping-live-hover")
// Ugly, but this is real JSON copied from a real live running app. We want the
// mock app to return realistic results if possible
.getRequestMappings(
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@RequestMapping(\"/hello\")", "Path: [/hello](http://cfapps.io:1000/hello)\n" +
"\n" +
"Process ID: 70000\n" +
"\n" +
"Process Name: test-request-mapping-live-hover\n" +
"\n" +
"---\n" +
"\n" +
"Path: [/hello](http://cfapps.io:1001/hello)\n" +
"\n" +
"Process ID: 80000\n" +
"\n" +
"Process Name: test-request-mapping-live-hover\n" +
"\n" +
"---\n" +
"\n" +
"Path: [/hello](http://cfapps.io:1002/hello)\n" +
"\n" +
"Process ID: 90000\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
}