Refine request mappings

This commit is contained in:
BoykoAlex
2017-10-26 11:43:16 -04:00
parent c614d6059e
commit 29c2a6e2b7
12 changed files with 1244 additions and 193 deletions

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,20 +11,27 @@
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.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
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.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
@@ -32,17 +39,18 @@ 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.text.TextDocument;
import com.google.common.collect.ImmutableList;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* @author Martin Lippert
*/
@@ -58,7 +66,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<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return ImmutableList.of(hoverRange);
@@ -77,7 +85,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
try {
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
Optional<RequestMappingMethod> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
Optional<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
if (val.isPresent()) {
addHoverContent(val.get(), hoverContent);
@@ -97,25 +105,18 @@ public class RequestMappingHoverProvider implements HoverProvider {
return null;
}
private Optional<RequestMappingMethod> getRequestMappingMethodFromRunningApp(Annotation annotation,
private Optional<Tuple2<RequestMapping, SpringBootApp>> getRequestMappingMethodFromRunningApp(Annotation annotation,
SpringBootApp[] runningApps) {
try {
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)) {
return Optional.of(new RequestMappingMethod(path, parsedMethod, app));
}
}
}
Collection<RequestMapping> mappings = app.getRequestMappings();
if (mappings!=null && !mappings.isEmpty()) {
return mappings.stream()
.filter(rm -> matchesAnnotation(annotation, rm))
.filter(rm -> methodMatchesAnnotation(annotation, rm))
.map(rm -> Tuples.of(rm, app))
.findFirst();
}
}
} catch (Exception e) {
@@ -124,16 +125,19 @@ public class RequestMappingHoverProvider implements HoverProvider {
return Optional.empty();
}
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);
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
&& binding.getName().equals(rm.getMethodName())
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
.map(t -> t.getQualifiedName())
.toArray(String[]::new),
rm.getMethodParameters());
} else if (parent instanceof TypeDeclaration) {
TypeDeclaration typeDec = (TypeDeclaration) parent;
return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
@@ -141,113 +145,156 @@ 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(Tuple2<RequestMapping, SpringBootApp> mappingMethod, List<Either<String, MarkedString>> hoverContent) throws Exception {
String processId = mappingMethod.getT2().getProcessID();
String processName = mappingMethod.getT2().getProcessName();
String port = mappingMethod.getT2().getPort();
String host = mappingMethod.getT2().getHost();
StringBuilder builder = new StringBuilder();
String port = mappingMethod.app.getPort();
String host = mappingMethod.app.getHost();
String url = UrlUtil.createUrl(host, port, path);
Arrays.stream(mappingMethod.getT1().getSplitPath()).forEach(path -> {
builder.append("Path: ");
builder.append("[");
builder.append(path);
builder.append("]");
builder.append("(");
builder.append(url);
builder.append(")");
String url = UrlUtil.createUrl(host, port, path);
if (builder.length() > 0) {
builder.append("\n");
}
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));
}
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");
}
protected String getRequestMethod(SingleMemberAnnotation annotation) {
ITypeBinding type = annotation.resolveTypeBinding();
if (type != null) {
switch (type.getQualifiedName()) {
case Constants.SPRING_GET_MAPPING:
return "GET";
case Constants.SPRING_POST_MAPPING:
return "POST";
case Constants.SPRING_DELETE_MAPPING:
return "DELETE";
case Constants.SPRING_PUT_MAPPING:
return "PUT";
case Constants.SPRING_PATCH_MAPPING:
return "PATCH";
}
}
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;
private boolean matchesAnnotation(Annotation annotation, RequestMapping rm) {
String[] mappingPath = null;
Set<String> methods = null;
if (annotation instanceof SingleMemberAnnotation) {
Expression valueContent = ((SingleMemberAnnotation) annotation).getValue();
SingleMemberAnnotation singleAnnotation = (SingleMemberAnnotation) annotation;
Expression valueContent = singleAnnotation.getValue();
if (valueContent instanceof StringLiteral) {
mappingPath = ((StringLiteral)valueContent).getLiteralValue();
mappingPath = new String[] { ((StringLiteral)valueContent).getLiteralValue() };
}
String method = getRequestMethod(singleAnnotation);
if (method != null) {
methods = new HashSet<>();
methods.add(method);
}
}
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();
}
MemberValuePair pair = (MemberValuePair)value;
String name = pair.getName().toString();
switch (name) {
case "value":
case "path":
mappingPath = getPaths(pair.getValue());
break;
case "method":
methods = getRequestMethod(pair.getValue());
break;
}
}
}
}
return mappingPath != null ? jsonKey.contains(mappingPath) : false;
if (mappingPath != null) {
if (Arrays.equals(mappingPath, rm.getSplitPath())) {
if (methods == null || methods.isEmpty()) {
return true;
} else {
return methods.equals(rm.getRequestMethods());
}
}
}
return false;
}
public JSONObject[] getRequestMappingsFromProcesses(SpringBootApp[] runningApps) {
List<JSONObject> result = new ArrayList<>();
private static String getExpressionValueAsString(Expression exp) {
if (exp instanceof StringLiteral) {
return ((StringLiteral)exp).getLiteralValue();
} else if (exp instanceof QualifiedName) {
return getExpressionValueAsString(((QualifiedName)exp).getName());
} else if (exp instanceof SimpleName) {
return ((SimpleName)exp).getIdentifier();
} else {
return null;
}
}
try {
for (SpringBootApp app : runningApps) {
String mappings = app.getRequestMappings();
if (mappings != null) {
JSONObject requestMappings = new JSONObject(mappings);
if (requestMappings != null) {
result.add(requestMappings);
}
}
@SuppressWarnings("unchecked")
private static String[] getPaths(Expression exp) {
if (exp instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) exp;
return ((List<Expression>)array.expressions()).stream()
.map(e -> getExpressionValueAsString(e))
.filter(Objects::nonNull)
.toArray(String[]::new);
} else {
String rm = getExpressionValueAsString(exp);
if (rm != null) {
return new String[] { rm };
}
}
catch (Exception e) {
Log.log(e);
}
return null;
}
return result.toArray(new JSONObject[result.size()]);
@SuppressWarnings("unchecked")
private static Set<String> getRequestMethod(Expression exp) {
if (exp instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) exp;
return ((List<Expression>)array.expressions()).stream()
.map(e -> getExpressionValueAsString(e))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
} else {
String rm = getExpressionValueAsString(exp);
if (rm != null) {
HashSet<String> methods = new HashSet<>();
methods.add(rm);
}
}
return null;
}
static class RequestMappingMethod {
public final SpringBootApp app;
public final String requestMappingPath;
public final JLRMethod requestMappingMethod;
public final RequestMapping requestMapping;
public RequestMappingMethod(String requestMappingPath, JLRMethod requestMappingMethod, SpringBootApp app) {
this.requestMappingPath = requestMappingPath;
this.requestMappingMethod = requestMappingMethod;
public RequestMappingMethod(RequestMapping requestMapping, SpringBootApp app) {
this.requestMapping = requestMapping;
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

@@ -132,4 +132,802 @@ public class RequestMappingLiveHoverTest {
}
@Test
public void testDeleteMappingHoverHintMethod1() 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\")", "Path: [/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=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String 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.assertNoHover("@DeleteMapping(\"/greetings\")");
}
@Test
public void testGetMappingHoverHintMethod1() 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=[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 java.lang.String;\n" +
"import org.springframework.stereotype.Controller;\n" +
"import org.springframework.web.bind.annotation.GetMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@GetMapping(\"/greetings\")\n" +
"public String greetings() {\n" +
"return \"Greetings!\";\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@GetMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testGetMappingHoverHintMethod2() 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 java.lang.String com.example.RestApi.greetings()\"}}")
. 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.GetMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@GetMapping(\"/greetings\")\n" +
"public String greetings() {\n" +
"return \"Greetings!\";\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@GetMapping(\"/greetings\")");
}
@Test
public void testPostMappingHoverHintMethod1() 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=[POST]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.createGreetings()\"}}")
. 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.PostMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PostMapping(\"/greetings\")\n" +
"public void createGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@PostMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testPostMappingHoverHintMethod2() 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=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.createGreetings()\"}}")
. 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.PostMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PostMapping(\"/greetings\")\n" +
"public void createGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@PostMapping(\"/greetings\")");
}
@Test
public void testPutMappingHoverHintMethod1() 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 void com.example.RestApi.updateGreetings()\"}}")
. 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.PutMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PutMapping(\"/greetings\")\n" +
"public void updateGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@PutMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testPutMappingHoverHintMethod2() 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=[GET]}\": {\"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 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 void updateGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@PutMapping(\"/greetings\")");
}
@Test
public void testPatchMappingHoverHintMethod1() 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=[PATCH]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.patchGreetings()\"}}")
. 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.PatchMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PatchMapping(\"/greetings\")\n" +
"public void patchGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@PatchMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testPatchMappingHoverHintMethod2() 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=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.patchGreetings()\"}}")
. 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.PatchMapping;\n" +
"\n" +
"@Controller\n" +
"public class RestApi {\n" +
"\n" +
"@PatchMapping(\"/greetings\")\n" +
"public void patchGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@PatchMapping(\"/greetings\")");
}
@Test
public void testMultiRequestMethodMappingHoverHintMethod1() 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=[POST,PUT]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.updateGreetings()\"}}")
. 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\", method={POST, PUT})\n" +
"public void updateGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertHoverContains("@RequestMapping(value=\"/greetings\", method={POST, PUT})", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMultiRequestMethodMappingHoverHintMethod2() 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=[POST,PUT,PATCH]}\": {\"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 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\", method={POST, PUT})\n" +
"public void updateGreetings() {\n" +
"}\n" +
"\n" +
"}",
docUri);
editor.assertNoHover("@RequestMapping(value=\"/greetings\", method={POST, PUT})");
}
@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)", "Path: [/greetings](http://cfapps.io:999/greetings)\n" +
"Path: [/hello](http://cfapps.io:999/hello)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
@Test
public void testMultiPathMappingHoverHintMethod2() 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 || /helloAgain],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.assertNoHover("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)");
}
@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)", "Path: [/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)", "Path: [/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)", "Path: [/find](http://cfapps.io:999/find)\n" +
"\n" +
"Process ID: 76543\n" +
"\n" +
"Process Name: test-request-mapping-live-hover");
}
}

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,8 +19,8 @@ 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.util.ExceptionUtil;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness.Builder;
import com.google.common.collect.ImmutableList;
@@ -97,7 +97,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;
}