Merge branch 'master' of github.com:spring-projects/sts4

This commit is contained in:
Kris De Volder
2017-10-27 13:19:53 -07:00
15 changed files with 991 additions and 296 deletions

View File

@@ -30,6 +30,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.utils.HoverContentUtils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
@@ -39,8 +40,8 @@ import com.google.common.collect.ImmutableList;
/**
*
* Provides live hovers and hints for @ConditionalOn... Spring Boot annotations from running
* spring boot apps.
* Provides live hovers and hints for @ConditionalOn... Spring Boot annotations
* from running spring boot apps.
*/
public class ConditionalsLiveHoverProvider implements HoverProvider {
@@ -98,14 +99,9 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
List<Either<String, MarkedString>> hoverContent) throws Exception {
for (int i = 0; i < conditions.size(); i++) {
RunningAppConditional condition = conditions.get(i);
hoverContent.add(Either.forLeft("Condition: " + condition.condition));
hoverContent.add(Either.forLeft("Message: " + condition.message));
// If there is more than one instances show process information
if (conditions.size() > 1) {
hoverContent.add(Either.forLeft("Process ID: " + condition.app.getProcessID()));
hoverContent.add(Either.forLeft("Process Name: " + condition.app.getProcessName()));
}
hoverContent.add(Either.forLeft(condition.message));
hoverContent.add(Either
.forLeft(HoverContentUtils.getProcessInformation(condition.app)));
if (i < conditions.size() - 1) {
hoverContent.add(Either.forLeft("---"));

View File

@@ -11,15 +11,15 @@
package org.springframework.ide.vscode.boot.java.requestmapping;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.json.JSONObject;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.util.Log;
@@ -44,7 +44,12 @@ public class LiveAppURLSymbolProvider {
SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
for (SpringBootApp app : runningApps) {
try {
collectLiveAppSymbols(result, app);
String host = app.getHost();
String port = app.getPort();
Stream<String> urls = app.getRequestMappings().stream()
.flatMap(rm -> Arrays.stream(rm.getSplitPath()))
.map(path -> UrlUtil.createUrl(host, port, path));
urls.forEach(url -> result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))));
}
catch (Exception e) {
Log.log(e);
@@ -57,21 +62,4 @@ public class LiveAppURLSymbolProvider {
return result;
}
private void collectLiveAppSymbols(List<SymbolInformation> result, SpringBootApp app) throws Exception {
String mappings = app.getRequestMappings();
JSONObject requestMappings = new JSONObject(mappings);
Iterator<String> keys = requestMappings.keys();
while (keys.hasNext()) {
String key = keys.next();
String extractedPath = UrlUtil.extractPath(key);
if (extractedPath != null) {
String[] splitPath = UrlUtil.splitPath(extractedPath);
for (String path : splitPath) {
String url = UrlUtil.createUrl(app.getHost(), app.getPort(), path);
result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1)))));
}
}
}
}
}

View File

@@ -11,38 +11,36 @@
package org.springframework.ide.vscode.boot.java.requestmapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.MarkedString;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.json.JSONObject;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser;
import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* @author Martin Lippert
*/
@@ -58,8 +56,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
if (runningApps.length > 0) {
Optional<List<RequestMappingMethod>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (!val.isEmpty()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(hoverRange);
}
@@ -77,10 +75,10 @@ public class RequestMappingHoverProvider implements HoverProvider {
try {
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
Optional<List<RequestMappingMethod>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
addHoverContent(val.get(), hoverContent);
if (!val.isEmpty()) {
addHoverContent(val, hoverContent);
}
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
@@ -97,171 +95,79 @@ public class RequestMappingHoverProvider implements HoverProvider {
return null;
}
private Optional<List<RequestMappingMethod>> getRequestMappingMethodFromRunningApp(Annotation annotation,
private List<Tuple2<RequestMapping, SpringBootApp>> getRequestMappingMethodFromRunningApp(Annotation annotation,
SpringBootApp[] runningApps) {
List<Tuple2<RequestMapping, SpringBootApp>> results = new ArrayList<>();
try {
List<RequestMappingMethod> methods = new ArrayList<>();
for (SpringBootApp app : runningApps) {
String mappings = app.getRequestMappings();
if (mappings != null) {
JSONObject requestMappings = new JSONObject(mappings);
String rawPath = getRawPath(annotation, requestMappings);
if (rawPath != null) {
String path = UrlUtil.extractPath(rawPath);
if (path != null) {
String rawMethod = getRawMethod(annotation, requestMappings);
JLRMethod parsedMethod = JLRMethodParser.parse(rawMethod);
if (methodMatchesAnnotation(annotation, parsedMethod)) {
methods.add(new RequestMappingMethod(path, parsedMethod, app));
}
}
}
Collection<RequestMapping> mappings = app.getRequestMappings();
if (mappings != null && !mappings.isEmpty()) {
mappings.stream()
.filter(rm -> methodMatchesAnnotation(annotation, rm))
.map(rm -> Tuples.of(rm, app))
.findFirst().ifPresent(t -> results.add(t));
}
}
if (!methods.isEmpty()) {
return Optional.of(methods);
}
} catch (Exception e) {
Log.log(e);
}
return Optional.empty();
return results;
}
private boolean methodMatchesAnnotation(Annotation annotation, JLRMethod requestMappingMethod) {
String rqClassName = requestMappingMethod.getFQClassName();
String rqMethod = requestMappingMethod.getMethodName();
private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) {
String rqClassName = rm.getFullyQualifiedClassName();
ASTNode parent = annotation.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration methodDec = (MethodDeclaration) parent;
IMethodBinding binding = methodDec.resolveBinding();
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName) &&
binding.getName().equals(rqMethod);
} else if (parent instanceof TypeDeclaration) {
TypeDeclaration typeDec = (TypeDeclaration) parent;
return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
&& binding.getName().equals(rm.getMethodName())
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
.map(t -> t.getTypeDeclaration().getQualifiedName())
.toArray(String[]::new),
rm.getMethodParameters());
// } else if (parent instanceof TypeDeclaration) {
// TypeDeclaration typeDec = (TypeDeclaration) parent;
// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
}
return false;
}
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;
private void addHoverContent(List<Tuple2<RequestMapping, SpringBootApp>> mappingMethods, List<Either<String, MarkedString>> hoverContent) throws Exception {
for (int i = 0; i < mappingMethods.size(); i++) {
Tuple2<RequestMapping, SpringBootApp> mappingMethod = mappingMethods.get(i);
StringBuilder builder = new StringBuilder();
String processId = mappingMethod.getT2().getProcessID();
String processName = mappingMethod.getT2().getProcessName();
String port = mappingMethod.getT2().getPort();
String host = mappingMethod.getT2().getHost();
String port = method.app.getPort();
String host = method.app.getHost();
String url = UrlUtil.createUrl(host, port, path);
List<Renderable> renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).flatMap(path -> {
String url = UrlUtil.createUrl(host, port, path);
StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(url);
builder.append("]");
builder.append("(");
builder.append(url);
builder.append(")");
return Stream.of(Renderables.text(builder.toString()), Renderables.lineBreak());
})
.collect(Collectors.toList());
builder.append("[");
builder.append(url);
builder.append("]");
builder.append("(");
builder.append(url);
builder.append(")");
// Remove the last line break
renderableUrls.remove(renderableUrls.size() - 1);
hoverContent.add(Either.forLeft(builder.toString()));
hoverContent.add(Either.forLeft(Renderables.concat(renderableUrls).toMarkdown()));
hoverContent.add(Either.forLeft("Process ID: " + processId));
hoverContent.add(Either.forLeft("Process Name: " + processName));
if (i < mappingMethods.size() - 1) {
if (i < mappingMethods.size() - 1) {
// Three dashes == line separator in Markdown
hoverContent.add(Either.forLeft("---"));
}
}
}
private String getRawMethod(Annotation annotation, JSONObject mappings) {
Iterator<String> keys = mappings.keys();
while (keys.hasNext()) {
String key = keys.next();
if (matchesAnnotation(annotation, key)) {
Object ob = mappings.get(key);
if (ob instanceof JSONObject) {
JSONObject methodMap = (JSONObject) ob;
return methodMap.getString("method");
}
}
}
return null;
}
private String getRawPath(Annotation annotation, JSONObject mappings) {
Iterator<String> keys = mappings.keys();
while (keys.hasNext()) {
String key = keys.next();
if (matchesAnnotation(annotation, key)) {
return key;
}
}
return null;
}
private boolean matchesAnnotation(Annotation annotation, String jsonKey) {
String mappingPath = null;
if (annotation instanceof SingleMemberAnnotation) {
Expression valueContent = ((SingleMemberAnnotation) annotation).getValue();
if (valueContent instanceof StringLiteral) {
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();
if (name != null && name.equals("value")) {
Expression valueContent = ((MemberValuePair) value).getValue();
if (valueContent instanceof StringLiteral) {
mappingPath = ((StringLiteral) valueContent).getLiteralValue();
}
}
}
}
}
return mappingPath != null ? jsonKey.contains(mappingPath) : false;
}
public JSONObject[] getRequestMappingsFromProcesses(SpringBootApp[] runningApps) {
List<JSONObject> result = new ArrayList<>();
try {
for (SpringBootApp app : runningApps) {
String mappings = app.getRequestMappings();
if (mappings != null) {
JSONObject requestMappings = new JSONObject(mappings);
if (requestMappings != null) {
result.add(requestMappings);
}
}
}
}
catch (Exception e) {
Log.log(e);
}
return result.toArray(new JSONObject[result.size()]);
}
static class RequestMappingMethod {
public final SpringBootApp app;
public final String requestMappingPath;
public final JLRMethod requestMappingMethod;
public RequestMappingMethod(String requestMappingPath, JLRMethod requestMappingMethod, SpringBootApp app) {
this.requestMappingPath = requestMappingPath;
this.requestMappingMethod = requestMappingMethod;
this.app = app;
}
}
}

View File

@@ -10,41 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.requestmapping;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class UrlUtil {
public static Stream<String> processOrPaths(String pathExp) {
if (pathExp.contains("||")) {
String[] paths = pathExp.split(Pattern.quote("||"));
return Stream.of(paths).map(String::trim);
} else {
return Stream.of(pathExp);
}
}
public static String extractPath(String key) {
if (key.startsWith("{[")) { //Case 2 (see above)
//An almost json string. Unfortunately not really json so we can't
//use org.json or jackson Mapper to properly parse this.
int start = 2; //right after first '['
int end = key.indexOf(']');
if (end>=2) {
return key.substring(start, end);
}
}
//Case 1, or some unanticipated stuff.
//Assume the key is the path, which is right for Case 1
// and probably more useful than null for 'unanticipated stuff'.
return key;
}
/**
* Creates http URL string based on host, port and path
* @param host
@@ -67,32 +34,4 @@ public class UrlUtil {
return null;
}
public static String[] splitPath(String path) {
if (path.contains("||")) {
List<String> result = new ArrayList<>();
String basePath = path.substring(0, path.indexOf("||")).trim();
result.add(basePath);
if (basePath.lastIndexOf('/') > 0) {
basePath = basePath.substring(0, basePath.lastIndexOf('/'));
}
String additionalPaths = path.substring(path.indexOf("||"));
StringTokenizer tokenizer = new StringTokenizer(additionalPaths, "||");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (token.length() > 0) {
result.add(basePath + "/" + token);
}
}
return result.toArray(new String[result.size()]);
}
else {
return new String[] {path};
}
}
}

View File

@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
public class HoverContentUtils {
public static String getProcessInformation(SpringBootApp app) {
return "Process " + app.getProcessID() + ": " + app.getProcessName();
}
}

View File

@@ -74,9 +74,10 @@ public class ConditionalsLiveHoverTest {
harness.intialize(directory);
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'");
editor.assertHoverContains("@ConditionalOnBean",
"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\n" +
"\n" +
"Process 22022: test-conditionals-live-hover");
}
@Test
@@ -97,8 +98,9 @@ public class ConditionalsLiveHoverTest {
harness.intialize(directory);
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans");
editor.assertHoverContains("@ConditionalOnMissingBean", "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n"+
"\n" +
"Process 22022: test-conditionals-live-hover");
}
@@ -120,20 +122,26 @@ public class ConditionalsLiveHoverTest {
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'");
editor.assertHoverContains("@ConditionalOnBean", "@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\n" +
"\n" +
"Process 22022: test-conditionals-live-hover");
editor.assertHoverContains("@ConditionalOnWebApplication", "Condition: OnWebApplicationCondition\n" + "\n"
+ "Message: @ConditionalOnWebApplication (required) found StandardServletEnvironment");
editor.assertHoverContains("@ConditionalOnWebApplication", "@ConditionalOnWebApplication (required) found StandardServletEnvironment\n"+
"\n" +
"Process 22022: test-conditionals-live-hover");
editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)",
"Condition: OnJavaCondition\n" + "\n" + "Message: @ConditionalOnJava (1.8 or newer) found 1.8");
"@ConditionalOnJava (1.8 or newer) found 1.8\n" +
"\n" +
"Process 22022: test-conditionals-live-hover");
editor.assertHoverContains("@ConditionalOnMissingClass", "Condition: OnClassCondition\n" + "\n"
+ "Message: @ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class");
editor.assertHoverContains("@ConditionalOnMissingClass", "@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\n" +
"\n" +
"Process 22022: test-conditionals-live-hover");
editor.assertHoverContains("@ConditionalOnExpression", "Condition: OnExpressionCondition\n" + "\n"
+ "Message: @ConditionalOnExpression (#{true}) resulted in true");
editor.assertHoverContains("@ConditionalOnExpression", "@ConditionalOnExpression (#{true}) resulted in true\n" +
"\n" +
"Process 22022: test-conditionals-live-hover");
}
@@ -169,30 +177,21 @@ public class ConditionalsLiveHoverTest {
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
editor.assertHoverContains("@ConditionalOnMissingBean", "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
"\n" +
"Process ID: 70000\n" +
"\n" +
"Process Name: test-conditionals-live-hover\n" +
"Process 70000: test-conditionals-live-hover\n" +
"\n" +
"---\n" +
"\n" +
"Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
"\n" +
"Process ID: 80000\n" +
"\n" +
"Process Name: test-conditionals-live-hover\n" +
"Process 80000: test-conditionals-live-hover\n" +
"\n" +
"---\n" +
"\n" +
"Condition: OnBeanCondition\n" + "\n"
+ "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" +
"\n" +
"Process ID: 90000\n" +
"\n" +
"Process Name: test-conditionals-live-hover");
"Process 90000: test-conditionals-live-hover");
}

View File

@@ -62,7 +62,7 @@ public class RequestMappingLiveHoverTest {
harness.intialize(directory);
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
editor.assertHoverContains("@RequestMapping(\"/hello-world\")", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" +
editor.assertHoverContains("@RequestMapping(method=RequestMethod.GET)", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" +
"\n" +
"Process ID: 22022\n" +
"\n" +
@@ -132,6 +132,555 @@ public class RequestMappingLiveHoverTest {
}
@Test
public void testSimpleMethodHoverHintMethod1() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.deleteGreetings()\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.DeleteMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@DeleteMapping(\"/greetings\")\n" +
"public void deleteGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@DeleteMapping(\"/greetings\")", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testDeleteMappingHoverHintMethod2() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.deleteGreetings()\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.DeleteMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@DeleteMapping(\"/greetings\")\n" +
"public void deleteGreetings(int n) {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@DeleteMapping(\"/greetings\")");
}
@Test
public void testNoHoverHintMethod() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/greetings],methods=[PUT]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.updateGreetings()\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import java.lang.String;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.PutMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PutMapping(\"/greetings\")\n" +
"public String updateGreetings(int n) {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@PutMapping(\"/greetings\")");
}
@Test
public void testMultiPathMappingHoverHintMethod1() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/greetings || /hello],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)\n" +
"public String greetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings) \n" +
"[http://cfapps.io:999/hello](http://cfapps.io:999/hello)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMethodMatchingHoverHintMethod1() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public org.springframework.http.ResponseEntity<?> com.example.RestApi.find(java.lang.String,java.util.Date,java.lang.String)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import java.util.Date;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public ResponseEntity<?> find(String p1, Date p2, String p3) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMethodMatchingHoverHintMethod2() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.lang.String>)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import java.util.Map;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String p1, Map<String, String> p2) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMethodMatchingHoverHintMethod3() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.lang.Integer>>)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import java.lang.Integer;\n" +
"import java.util.Map;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String p1, Map<String, Map<String, Integer>> p2) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testWildCardMethodMatchingHoverHint() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.util.Map<java.lang.String, ? extends java.lang.Integer>>)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import java.lang.Integer;\n" +
"import java.util.Map;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String p1, Map<String, Map<String, ? extends Integer>> p2) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testArrayMethodMatchingHoverHint() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String[])\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String[] p) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMultiDimensionalArrayMethodMatchingHoverHint() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String[][])\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String[][] p) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testVarArgsMethodMatchingHoverHint() 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 a mock running boot app
mockAppProvider.builder()
.isSpringBootApp(true)
.port("999")
.processId("76543")
.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(
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String...)\"}}")
. build();
harness.intialize(directory);
Editor editor = harness.newEditor(LanguageId.JAVA,
"package com.example;\n" +
"\n" +
"import org.springframework.http.ResponseEntity;\n" +
"import java.lang.String;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@RequestMapping(value=\"/find\", method=GET)\n" +
"public Object set(String... p) {\n" +
"return null;\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMultipleAppsLiveHover() throws Exception {

View File

@@ -1,58 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.requestmapping.test;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.ide.vscode.boot.java.requestmapping.UrlUtil;
/**
* @author Martin Lippert
*/
public class UrlUtilTest {
@Test
public void testSplitPathWithoutDuplicate() {
String path = "/superpath";
String[] splitPath = UrlUtil.splitPath(path);
assertEquals(1, splitPath.length);
assertEquals("/superpath", splitPath[0]);
}
@Test
public void testSplitPathSimpleCaseWithEmptyOr() {
String path = "/superpath/mypath || ";
String[] splitPath = UrlUtil.splitPath(path);
assertEquals(1, splitPath.length);
assertEquals("/superpath/mypath", splitPath[0]);
}
@Test
public void testSplitPathSimpleCase() {
String path = "/superpath/mypath || mypath.json";
String[] splitPath = UrlUtil.splitPath(path);
assertEquals(2, splitPath.length);
assertEquals("/superpath/mypath", splitPath[0]);
assertEquals("/superpath/mypath.json", splitPath[1]);
}
@Test
public void testSplitPathMultipleCases() {
String path = "/superpath/mypath || mypath.json || somethingelse.what";
String[] splitPath = UrlUtil.splitPath(path);
assertEquals(3, splitPath.length);
assertEquals("/superpath/mypath", splitPath[0]);
assertEquals("/superpath/mypath.json", splitPath[1]);
assertEquals("/superpath/somethingelse.what", splitPath[2]);
}
}

View File

@@ -19,9 +19,9 @@ import java.util.Collection;
import org.mockito.Mockito;
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness.Builder;
import com.google.common.collect.ImmutableList;
@@ -102,7 +102,8 @@ public class MockRunningAppProvider {
}
public MockAppBuilder getRequestMappings(String mappings) throws Exception {
when(app.getRequestMappings()).thenReturn(mappings);
Collection<RequestMapping> requestMappings = SpringBootApp.parseRequestMappingsJson(mappings);
when(app.getRequestMappings()).thenReturn(requestMappings);
return this;
}