From 7328932b7ca70fa77af48046455f91eb898cea79 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 8 Mar 2018 13:28:23 +0100 Subject: [PATCH] added media types to webflux symbol and code lens logic --- .../java/requestmapping/MediaTypeMapping.java | 33 +++++ .../WebfluxAcceptTypeFinder.java | 50 +++++++ .../WebfluxContentTypeFinder.java | 55 ++++++++ .../WebfluxHandlerCodeLensProvider.java | 18 ++- .../requestmapping/WebfluxMethodFinder.java | 19 +-- .../requestmapping/WebfluxPathFinder.java | 2 +- .../WebfluxRouterSymbolProvider.java | 125 ++++++++++++++---- .../java/requestmapping/WebfluxUtils.java | 37 +++++- .../test/WebFluxCodeLensProviderTest.java | 8 +- .../WebFluxMappingSymbolProviderTest.java | 20 +-- .../test-webflux-project/pom.xml | 44 +----- .../main/java/org/test/RouterExperiments.java | 34 +++++ .../src/main/java/org/test/TradingUser.java | 72 ---------- .../java/org/test/TradingUserRepository.java | 11 -- .../main/java/org/test/UserController.java | 20 +-- 15 files changed, 345 insertions(+), 203 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/MediaTypeMapping.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java create mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/RouterExperiments.java delete mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUser.java delete mode 100644 headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUserRepository.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/MediaTypeMapping.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/MediaTypeMapping.java new file mode 100644 index 000000000..f834f255d --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/MediaTypeMapping.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 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; + +/** + * @author Martin Lippert + */ +public enum MediaTypeMapping { + + TEXT_PLAIN("text/plain"), + APPLICATION_JSON("application/json"), + APPLICATION_STREAM_JSON("application/stream+json"); + + + private String mediaType; + + private MediaTypeMapping(String mediaType) { + this.mediaType = mediaType; + } + + public String getMediaType() { + return mediaType; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java new file mode 100644 index 000000000..f711b7281 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxAcceptTypeFinder.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2018 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; + +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; + +/** + * @author Martin Lippert + */ +public class WebfluxAcceptTypeFinder extends ASTVisitor { + + private String acceptType; + + public WebfluxAcceptTypeFinder() { + } + + public String getAcceptType() { + return acceptType; + } + + @Override + public boolean visit(MethodInvocation node) { + boolean visitChildren = true; + + IMethodBinding methodBinding = node.resolveMethodBinding(); + + if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { + String name = methodBinding.getName(); + if (name != null && WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(name)) { + acceptType = WebfluxUtils.extractSimpleNameArgument(node); + } + } + + if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { + visitChildren = false; + } + return visitChildren; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java new file mode 100644 index 000000000..e104c3a55 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxContentTypeFinder.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2018 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; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; + +/** + * @author Martin Lippert + */ +public class WebfluxContentTypeFinder extends ASTVisitor { + + private String contentType; + private ASTNode root; + + public WebfluxContentTypeFinder(ASTNode root) { + this.root = root; + } + + public String getContentType() { + return contentType; + } + + @Override + public boolean visit(MethodInvocation node) { + boolean visitChildren = true; + + if (node != this.root) { + IMethodBinding methodBinding = node.resolveMethodBinding(); + + if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { + String name = methodBinding.getName(); + if (name != null && WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(name)) { + contentType = WebfluxUtils.extractSimpleNameArgument(node); + } + } + + if (WebfluxUtils.isRouteMethodInvocation(methodBinding)) { + visitChildren = false; + } + } + return visitChildren; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java index 14593c14f..73950b090 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxHandlerCodeLensProvider.java @@ -75,8 +75,8 @@ public class WebfluxHandlerCodeLensProvider implements CodeLensProvider { String codeLensCommand = handlerInfo.getHttpMethod() != null ? handlerInfo.getHttpMethod() + " " : ""; codeLensCommand += handlerInfo.getPath(); - codeLensCommand += handlerInfo.getContentType() != null ? " Content-Type: " + handlerInfo.getContentType() : ""; - codeLensCommand += handlerInfo.getAcceptType() != null ? " Accept: " + handlerInfo.getAcceptType() : ""; + codeLensCommand += handlerInfo.getAcceptType() != null ? " - Accept: " + getMediaType(handlerInfo.getAcceptType()) : ""; + codeLensCommand += handlerInfo.getContentType() != null ? " - Content-Type: " + getMediaType(handlerInfo.getContentType()) : ""; codeLens.setCommand(new Command(codeLensCommand, null)); @@ -88,5 +88,19 @@ public class WebfluxHandlerCodeLensProvider implements CodeLensProvider { } } } + + protected String getMediaType(String handlerInfo) { + if (handlerInfo == null) { + return null; + } + + try { + MediaTypeMapping mediaType = MediaTypeMapping.valueOf(handlerInfo); + return mediaType.getMediaType(); + } + catch (IllegalArgumentException e) { + return handlerInfo; + } + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java index a8a48ef55..bef4fe7ba 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxMethodFinder.java @@ -10,13 +10,10 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.List; - import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; -import org.eclipse.jdt.core.dom.QualifiedName; /** * @author Martin Lippert @@ -47,7 +44,7 @@ public class WebfluxMethodFinder extends ASTVisitor { method = name; } else if (name != null && WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(name)) { - method = extractMethodValue(node); + method = WebfluxUtils.extractQualifiedNameArgument(node); } } @@ -58,18 +55,4 @@ public class WebfluxMethodFinder extends ASTVisitor { return visitChildren; } - private String extractMethodValue(MethodInvocation node) { - List arguments = node.arguments(); - if (arguments != null && arguments.size() > 0) { - Object object = arguments.get(0); - if (object instanceof QualifiedName) { - QualifiedName qualifiedName = (QualifiedName) object; - if (qualifiedName.getName() != null) { - return qualifiedName.getName().toString(); - } - } - } - return null; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java index 20260238f..60a323295 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxPathFinder.java @@ -41,7 +41,7 @@ public class WebfluxPathFinder extends ASTVisitor { if (WebfluxUtils.REQUEST_PREDICATES_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { String name = methodBinding.getName(); if (name != null && WebfluxUtils.REQUEST_PREDICATE_ALL_PATH_METHODS.contains(name)) { - path = WebfluxUtils.extractPath(node); + path = WebfluxUtils.extractStringLiteralArgument(node); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java index e9b494bb5..d7394dab6 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouterSymbolProvider.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.function.Function; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; @@ -33,6 +34,9 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; + /** * @author Martin Lippert */ @@ -87,12 +91,11 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } protected void extractMappingSymbol(MethodInvocation node, TextDocument doc, List result) { - String foundPath = extractPathFromRouterFunction(node); - String path = extractPath(node, foundPath); + String path = extractPath(node); String httpMethod = extractMethod(node); - String contentType = null; - String acceptType = null; + String contentType = extractContentType(node); + String acceptType = extractAcceptType(node); int methodNameStart = node.getName().getStartPosition(); int invocationStart = node.getStartPosition(); @@ -111,19 +114,102 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { } } - private String extractPathFromRouterFunction(MethodInvocation routerInvocation) { + private String extractPath(MethodInvocation routerInvocation) { WebfluxPathFinder pathFinder = new WebfluxPathFinder(routerInvocation); routerInvocation.accept(pathFinder); String path = pathFinder.getPath(); if (path == null) path = ""; - return path; + return extractNestedValue(routerInvocation, path, (methodInvocationPathPrefix) -> { + IMethodBinding methodBinding = methodInvocationPathPrefix.getT1().resolveMethodBinding(); + String methodName = methodBinding.getName(); + + if (WebfluxUtils.REQUEST_PREDICATE_PATH_METHOD.equals(methodName)) { + String additionalPath = WebfluxUtils.extractStringLiteralArgument(methodInvocationPathPrefix.getT1()); + if (additionalPath != null && additionalPath.length() > 0) { + return additionalPath + methodInvocationPathPrefix.getT2(); + } + } + + return methodInvocationPathPrefix.getT2(); + }); } - private String extractPath(ASTNode node, String path) { + private String extractMethod(MethodInvocation routerInvocation) { + WebfluxMethodFinder methodFinder = new WebfluxMethodFinder(routerInvocation); + routerInvocation.accept(methodFinder); + + String method = methodFinder.getMethod(); + + return extractNestedValue(routerInvocation, method, (methodInvocationPathPrefix) -> { + IMethodBinding methodBinding = methodInvocationPathPrefix.getT1().resolveMethodBinding(); + String methodName = methodBinding.getName(); + + if (WebfluxUtils.REQUEST_PREDICATE_METHOD_METHOD.equals(methodName)) { + String newMethod = WebfluxUtils.extractStringLiteralArgument(methodInvocationPathPrefix.getT1()); + if (method == null) { + return newMethod; + } + } + + return methodInvocationPathPrefix.getT2(); + }); + } + + private String extractAcceptType(MethodInvocation routerInvocation) { + String acceptType = null; + + WebfluxAcceptTypeFinder acceptTypeFinder = new WebfluxAcceptTypeFinder(); + List arguments = routerInvocation.arguments(); + for (Object argument : arguments) { + if (argument != null && argument instanceof ASTNode) { + ((ASTNode)argument).accept(acceptTypeFinder); + if (acceptTypeFinder.getAcceptType() != null) { + acceptType = acceptTypeFinder.getAcceptType(); + } + } + } + + return extractNestedValue(routerInvocation, acceptType, (methodInvocationPathPrefix) -> { + IMethodBinding methodBinding = methodInvocationPathPrefix.getT1().resolveMethodBinding(); + String methodName = methodBinding.getName(); + + if (WebfluxUtils.REQUEST_PREDICATE_ACCEPT_TYPE_METHOD.equals(methodName)) { + String newAcceptType = WebfluxUtils.extractSimpleNameArgument(methodInvocationPathPrefix.getT1()); + if (newAcceptType != null) { + return newAcceptType; + } + } + + return methodInvocationPathPrefix.getT2(); + }); + } + + private String extractContentType(MethodInvocation routerInvocation) { + WebfluxContentTypeFinder contentTypeFinder = new WebfluxContentTypeFinder(routerInvocation); + routerInvocation.accept(contentTypeFinder); + + String contentType = contentTypeFinder.getContentType(); + + return extractNestedValue(routerInvocation, contentType, (methodInvocationPathPrefix) -> { + IMethodBinding methodBinding = methodInvocationPathPrefix.getT1().resolveMethodBinding(); + String methodName = methodBinding.getName(); + + if (WebfluxUtils.REQUEST_PREDICATE_CONTENT_TYPE_METHOD.equals(methodName)) { + String newContentType = WebfluxUtils.extractSimpleNameArgument(methodInvocationPathPrefix.getT1()); + if (contentType == null) { + return newContentType; + } + } + + return methodInvocationPathPrefix.getT2(); + }); + } + + private String extractNestedValue(ASTNode node, String value, Function, String> extractor) { if (node == null || node instanceof TypeDeclaration) { - return path; + return value; } if (node instanceof MethodInvocation) { @@ -132,38 +218,21 @@ public class WebfluxRouterSymbolProvider implements SymbolProvider { if (WebfluxUtils.ROUTER_FUNCTIONS_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { String name = methodBinding.getName(); - if ("nest".equals(name)) { + if (WebfluxUtils.REQUEST_PREDICATE_NEST_METHOD.equals(name)) { List arguments = methodInvocation.arguments(); for (Object argument : arguments) { if (argument instanceof MethodInvocation) { MethodInvocation nestedMethod = (MethodInvocation) argument; - IMethodBinding nestedMethodBinding = nestedMethod.resolveMethodBinding(); - - String nestedMethodName = nestedMethodBinding.getName(); - if ("path".equals(nestedMethodName)) { - String additionalPath = WebfluxUtils.extractPath(nestedMethod); - if (additionalPath != null && additionalPath.length() > 0) { - path = additionalPath + path; - } - } + value = extractor.apply(Tuples.of(nestedMethod, value)); } - } } } } - return extractPath(node.getParent(), path); + return extractNestedValue(node.getParent(), value, extractor); } - private String extractMethod(MethodInvocation routerInvocation) { - WebfluxMethodFinder methodFinder = new WebfluxMethodFinder(routerInvocation); - routerInvocation.accept(methodFinder); - - String method = methodFinder.getMethod(); - return method; - } - private WebfluxHandlerInformation extractHandlerInformation(MethodInvocation node, String path, String httpMethod, String contentType, String acceptType) { List arguments = node.arguments(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java index 6de785cda..51c1a7a1f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxUtils.java @@ -17,6 +17,8 @@ import java.util.Set; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; /** @@ -30,12 +32,15 @@ public class WebfluxUtils { public static final String REQUEST_PREDICATE_PATH_METHOD = "path"; public static final String REQUEST_PREDICATE_METHOD_METHOD = "method"; + public static final String REQUEST_PREDICATE_ACCEPT_TYPE_METHOD = "accept"; + public static final String REQUEST_PREDICATE_CONTENT_TYPE_METHOD = "contentType"; + public static final String REQUEST_PREDICATE_NEST_METHOD = "nest"; public static final Set REQUEST_PREDICATE_HTTPMETHOD_METHODS = new HashSet<>(Arrays.asList("GET", "POST", "DELETE", "PUT", "PATCH", "HEAD", "OPTIONS")); public static final Set REQUEST_PREDICATE_ALL_PATH_METHODS = new HashSet<>(Arrays.asList(REQUEST_PREDICATE_PATH_METHOD, "GET", "POST", "DELETE", "PUT", "PATCH", "HEAD", "OPTIONS")); - public static String extractPath(MethodInvocation node) { + public static String extractStringLiteralArgument(MethodInvocation node) { List arguments = node.arguments(); if (arguments != null && arguments.size() > 0) { Object object = arguments.get(0); @@ -47,6 +52,36 @@ public class WebfluxUtils { return null; } + public static String extractQualifiedNameArgument(MethodInvocation node) { + List arguments = node.arguments(); + if (arguments != null && arguments.size() > 0) { + Object object = arguments.get(0); + if (object instanceof QualifiedName) { + QualifiedName qualifiedName = (QualifiedName) object; + if (qualifiedName.getName() != null) { + return qualifiedName.getName().toString(); + } + } + } + return null; + } + + public static String extractSimpleNameArgument(MethodInvocation node) { + List arguments = node.arguments(); + if (arguments != null && arguments.size() > 0) { + Object object = arguments.get(0); + if (object instanceof SimpleName) { + SimpleName name = (SimpleName) object; + if (name.getFullyQualifiedName() != null) { + return name.getFullyQualifiedName().toString(); + } + } + } + return null; + } + + + public static boolean isRouteMethodInvocation(IMethodBinding methodBinding) { if (ROUTER_FUNCTIONS_TYPE.equals(methodBinding.getDeclaringClass().getBinaryName())) { String name = methodBinding.getName(); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java index 786a535c7..d9a8a2a9a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxCodeLensProviderTest.java @@ -53,10 +53,10 @@ public class WebFluxCodeLensProviderTest { assertEquals(4, codeLenses.size()); - assertTrue(containsCodeLens(codeLenses, "GET /hello", 25, 29, 25, 34)); - assertTrue(containsCodeLens(codeLenses, "POST /echo", 30, 29, 30, 33)); - assertTrue(containsCodeLens(codeLenses, "GET /quotes", 35, 29, 35, 41)); - assertTrue(containsCodeLens(codeLenses, "GET /quotes", 41, 29, 41, 40)); + assertTrue(containsCodeLens(codeLenses, "GET /hello - Accept: text/plain", 25, 29, 25, 34)); + assertTrue(containsCodeLens(codeLenses, "POST /echo - Accept: text/plain - Content-Type: text/plain", 30, 29, 30, 33)); + assertTrue(containsCodeLens(codeLenses, "GET /quotes - Accept: application/stream+json", 35, 29, 35, 41)); + assertTrue(containsCodeLens(codeLenses, "GET /quotes - Accept: application/json", 41, 29, 41, 40)); } private boolean containsCodeLens(List codeLenses, String commandTitle, int startLine, int startPosition, int endLine, int endPosition) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java index 00d7b7d5c..4d6221074 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/WebFluxMappingSymbolProviderTest.java @@ -46,8 +46,8 @@ public class WebFluxMappingSymbolProviderTest { String docUri = directory.toPath().resolve("src/main/java/org/test/UserController.java").toUri().toString(); List symbols = getSymbols(docUri); assertEquals(4, symbols.size()); - assertTrue(containsSymbol(symbols, "@/users", docUri, 19, 1, 19, 74)); - assertTrue(containsSymbol(symbols, "@/users/{username}", docUri, 24, 1, 24, 85)); + assertTrue(containsSymbol(symbols, "@/users", docUri, 13, 1, 13, 74)); + assertTrue(containsSymbol(symbols, "@/users/{username}", docUri, 18, 1, 18, 85)); List addons = getAdditionalInformation(docUri); assertNull(addons); @@ -73,15 +73,15 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("/hello", handlerInfo1.getPath()); assertEquals("GET", handlerInfo1.getHttpMethod()); assertNull(handlerInfo1.getContentType()); - assertNull(handlerInfo1.getAcceptType()); + assertEquals("TEXT_PLAIN", handlerInfo1.getAcceptType()); assertEquals("org.test.QuoteHandler", handlerInfo1.getHandlerClass()); assertEquals("public Mono hello(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo1.getHandlerMethod()); WebfluxHandlerInformation handlerInfo2 = getWebfluxHandler(addons, "/echo", "POST").get(0); assertEquals("/echo", handlerInfo2.getPath()); assertEquals("POST", handlerInfo2.getHttpMethod()); - assertNull(handlerInfo2.getContentType()); - assertNull(handlerInfo2.getAcceptType()); + assertEquals("TEXT_PLAIN", handlerInfo2.getContentType()); + assertEquals("TEXT_PLAIN", handlerInfo2.getAcceptType()); assertEquals("org.test.QuoteHandler", handlerInfo2.getHandlerClass()); assertEquals("public Mono echo(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo2.getHandlerMethod()); @@ -89,7 +89,7 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("/quotes", handlerInfo3.getPath()); assertEquals("GET", handlerInfo3.getHttpMethod()); assertNull(handlerInfo3.getContentType()); - assertNull(handlerInfo3.getAcceptType()); + assertEquals("APPLICATION_STREAM_JSON", handlerInfo3.getAcceptType()); assertEquals("org.test.QuoteHandler", handlerInfo3.getHandlerClass()); assertEquals("public Mono streamQuotes(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo3.getHandlerMethod()); @@ -97,7 +97,7 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("/quotes", handlerInfo4.getPath()); assertEquals("GET", handlerInfo4.getHttpMethod()); assertNull(handlerInfo4.getContentType()); - assertNull(handlerInfo4.getAcceptType()); + assertEquals("APPLICATION_JSON", handlerInfo4.getAcceptType()); assertEquals("org.test.QuoteHandler", handlerInfo4.getHandlerClass()); assertEquals("public Mono fetchQuotes(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo4.getHandlerMethod()); } @@ -121,14 +121,14 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("/person/{id}", handlerInfo1.getPath()); assertEquals("GET", handlerInfo1.getHttpMethod()); assertNull(handlerInfo1.getContentType()); - assertNull(handlerInfo1.getAcceptType()); + assertEquals("APPLICATION_JSON", handlerInfo1.getAcceptType()); assertEquals("org.test.PersonHandler", handlerInfo1.getHandlerClass()); assertEquals("public Mono getPerson(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo1.getHandlerMethod()); WebfluxHandlerInformation handlerInfo2 = getWebfluxHandler(addons, "/person/", "POST").get(0); assertEquals("/person/", handlerInfo2.getPath()); assertEquals("POST", handlerInfo2.getHttpMethod()); - assertNull(handlerInfo2.getContentType()); + assertEquals("APPLICATION_JSON", handlerInfo2.getContentType()); assertNull(handlerInfo2.getAcceptType()); assertEquals("org.test.PersonHandler", handlerInfo2.getHandlerClass()); assertEquals("public Mono createPerson(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo2.getHandlerMethod()); @@ -137,7 +137,7 @@ public class WebFluxMappingSymbolProviderTest { assertEquals("/person", handlerInfo3.getPath()); assertEquals("GET", handlerInfo3.getHttpMethod()); assertNull(handlerInfo3.getContentType()); - assertNull(handlerInfo3.getAcceptType()); + assertEquals("APPLICATION_JSON", handlerInfo3.getAcceptType()); assertEquals("org.test.PersonHandler", handlerInfo3.getHandlerClass()); assertEquals("public Mono listPeople(org.springframework.web.reactive.function.server.ServerRequest)", handlerInfo3.getHandlerMethod()); } diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/pom.xml b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/pom.xml index 930b1a47c..83db5d821 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/pom.xml +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.0.M1 + 2.0.0.RELEASE @@ -35,10 +35,6 @@ org.springframework.boot spring-boot-starter-webflux - - org.springframework.boot - spring-boot-starter-data-mongodb-reactive - @@ -50,42 +46,4 @@ - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - - spring-snapshots - Spring Snapshots - https://repo.spring.io/snapshot - - true - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/RouterExperiments.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/RouterExperiments.java new file mode 100644 index 000000000..5634bed69 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/RouterExperiments.java @@ -0,0 +1,34 @@ +package org.test; + +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; +import static org.springframework.web.reactive.function.server.RequestPredicates.POST; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; +import static org.springframework.web.reactive.function.server.RequestPredicates.method; +import static org.springframework.web.reactive.function.server.RequestPredicates.path; +import static org.springframework.web.reactive.function.server.RouterFunctions.nest; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Configuration +public class RouterExperiments { + + @Bean + public RouterFunction superRoutingFunction() { + PersonHandler handler = new PersonHandler(); + + return nest(path("/super"), + nest(path("/something"), + nest(accept(APPLICATION_JSON), + route(GET("/{id}"), handler::getPerson) + .andRoute(method(HttpMethod.GET), handler::listPeople) + ).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::createPerson))); + } + +} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUser.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUser.java deleted file mode 100644 index d25f38aa1..000000000 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUser.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.test; - -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document -public class TradingUser { - - @Id - private String id; - - private String userName; - - private String fullName; - - public TradingUser() { - } - - public TradingUser(String id, String userName, String fullName) { - this.id = id; - this.userName = userName; - this.fullName = fullName; - } - - public TradingUser(String userName, String fullName) { - this.userName = userName; - this.fullName = fullName; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - - public String getFullName() { - return fullName; - } - - public void setFullName(String fullName) { - this.fullName = fullName; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TradingUser that = (TradingUser) o; - - if (!id.equals(that.id)) return false; - return userName.equals(that.userName); - } - - @Override - public int hashCode() { - int result = id.hashCode(); - result = 31 * result + userName.hashCode(); - return result; - } -} - diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUserRepository.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUserRepository.java deleted file mode 100644 index 2cf539025..000000000 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/TradingUserRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.test; - -import reactor.core.publisher.Mono; - -import org.springframework.data.mongodb.repository.ReactiveMongoRepository; - -public interface TradingUserRepository extends ReactiveMongoRepository { - - Mono findByUserName(String userName); - -} diff --git a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/UserController.java b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/UserController.java index 64621ec86..9de96cbeb 100644 --- a/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/UserController.java +++ b/headless-services/spring-boot-language-server/src/test/resources/test-projects/test-webflux-project/src/main/java/org/test/UserController.java @@ -1,30 +1,24 @@ package org.test; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + @RestController public class UserController { - private final TradingUserRepository tradingUserRepository; - - public UserController(TradingUserRepository tradingUserRepository) { - this.tradingUserRepository = tradingUserRepository; - } - @GetMapping(path = "/users", produces = MediaType.APPLICATION_JSON_VALUE) - public Flux listUsers() { - return this.tradingUserRepository.findAll(); + public Flux listUsers() { + return null; } @GetMapping(path = "/users/{username}", produces = MediaType.APPLICATION_JSON_VALUE) - public Mono showUsers(@PathVariable String username) { - return this.tradingUserRepository.findByUserName(username); + public Mono showUsers(@PathVariable String username) { + return null; } } \ No newline at end of file