Skip to content

Commit

Permalink
feat: Support Reactive Routes
Browse files Browse the repository at this point in the history
Fixes #1114

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Aug 30, 2023
1 parent 3fbeb41 commit 95184a8
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public class DefaultJaxRsInfoProvider implements IJaxRsInfoProvider {

@Override
public boolean canProvideJaxRsMethodInfoForClass(PsiFile typeRoot, Module javaProject, ProgressIndicator monitor) {
return PsiTypeUtils.findType(javaProject, JAVAX_WS_RS_PATH_ANNOTATION) == null
|| PsiTypeUtils.findType(javaProject, JAKARTA_WS_RS_PATH_ANNOTATION) == null;
return PsiTypeUtils.findType(javaProject, JAVAX_WS_RS_PATH_ANNOTATION) != null
|| PsiTypeUtils.findType(javaProject, JAKARTA_WS_RS_PATH_ANNOTATION) != null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.microprofile.psi.internal.quarkus.vertx.java;

/**
* Constants important to working with Renarde applications.
*/
public class VertxWebConstants {

public static final String ROUTE_FQN = "io.quarkus.vertx.web.Route";

public static final String ROUTE_PATH = "path";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.microprofile.psi.internal.quarkus.vertx.java;


import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.psi.*;
import com.intellij.util.KeyedLazyInstanceEP;
import com.redhat.devtools.intellij.lsp4ij.LSPIJUtils;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.*;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.AnnotationUtils.hasAnnotation;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.PsiTypeUtils.overlaps;
import static com.redhat.microprofile.psi.internal.quarkus.vertx.java.VertxWebConstants.ROUTE_FQN;
import static com.redhat.microprofile.psi.internal.quarkus.vertx.java.VertxWebUtils.getRouteAnnotation;

/**
* Use custom logic for all JAX-RS features in classes that extends Renarde's
* <code>Controller</code> class.
*/
public class VertxWebJaxRsInfoProvider extends KeyedLazyInstanceEP<IJaxRsInfoProvider> implements IJaxRsInfoProvider {

private static final Logger LOGGER = Logger.getLogger(VertxWebJaxRsInfoProvider.class.getName());

@Override
public boolean canProvideJaxRsMethodInfoForClass(PsiFile typeRoot, Module javaProject, ProgressIndicator monitor) {
return PsiTypeUtils.findType(javaProject, ROUTE_FQN) != null;
}

@Override
public Set<PsiClass> getAllJaxRsClasses(Module javaProject, ProgressIndicator monitor) {
// TODO: implement when LSP4IJ will support workspace symbols
return Collections.emptySet();
}

@Override
public List<JaxRsMethodInfo> getJaxRsMethodInfo(PsiFile typeRoot, JaxRsContext jaxrsContext, IPsiUtils utils,
ProgressIndicator monitor) {
try {
PsiClass type = findFirstClass(typeRoot);
if (type == null) {
return Collections.emptyList();
}
String pathSegment = null;//JaxRsUtils.getJaxRsPathValue(type);
String typeSegment = type.getName();

List<JaxRsMethodInfo> methodInfos = new ArrayList<>();
for (PsiMethod method : type.getMethods()) {

if (method.isConstructor() || utils.isHiddenGeneratedElement(method)) {
continue;
}
// ignore element if method range overlaps the type range,
// happens for generated
// bytecode, i.e. with lombok
if (overlaps(type.getNameIdentifier().getTextRange(), method.getNameIdentifier().getTextRange())) {
continue;
}

//if (method.getModifierList().hasExplicitModifier(PsiModifier.PUBLIC)) {

PsiAnnotation routeAnnotation = getRouteAnnotation(method);
if (routeAnnotation == null) {
continue;
}

// @Route(path="/foo")
String methodSegment = VertxWebUtils.getRouteAnnotationPath(routeAnnotation);
if (methodSegment == null) {
methodSegment = method.getName();
}
String path;
if (pathSegment == null) {
path = methodSegment.startsWith("/") ? methodSegment : JaxRsUtils.buildURL(typeSegment, methodSegment);
} else {
path = JaxRsUtils.buildURL(pathSegment, methodSegment);
}
String url = JaxRsUtils.buildURL(jaxrsContext.getLocalBaseURL(), path);

JaxRsMethodInfo methodInfo = createMethodInfo(method, routeAnnotation, url);
if (methodInfo != null) {
methodInfos.add(methodInfo);
}
//}
}
return methodInfos;
} catch (ProcessCanceledException e) {
throw e;
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while collecting JAX-RS methods for Vertx Web @Route", e);
return Collections.emptyList();
}
}

private PsiClass findFirstClass(PsiFile typeRoot) {
for (PsiElement element:typeRoot.getChildren()) {
if (element instanceof PsiClass) {
return (PsiClass) element;
}
}
return null;
}

private static JaxRsMethodInfo createMethodInfo(PsiMethod method, PsiAnnotation routeAnnotation, String url) {

PsiFile resource = method.getContainingFile();
if (resource == null) {
return null;
}
String documentUri = LSPIJUtils.toUriAsString(resource);

HttpMethod httpMethod = HttpMethod.GET;
for (String methodAnnotationFQN : JaxRsConstants.HTTP_METHOD_ANNOTATIONS) {
if (hasAnnotation(method, methodAnnotationFQN)) {
httpMethod = VertxWebUtils.getHttpMethodForAnnotation(methodAnnotationFQN);
break;
}
}

return new JaxRsMethodInfo(url, httpMethod, method, documentUri);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*******************************************************************************
* Copyright (c) 2020 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.microprofile.psi.internal.quarkus.vertx.java;

import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.HttpMethod;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.JaxRsConstants;
import com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.IPsiUtils;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

import java.util.Collections;

import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.jaxrs.JaxRsConstants.*;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.core.utils.AnnotationUtils.*;
import static com.redhat.microprofile.psi.internal.quarkus.vertx.java.VertxWebConstants.ROUTE_FQN;
import static com.redhat.microprofile.psi.internal.quarkus.vertx.java.VertxWebConstants.ROUTE_PATH;


/**
* JAX-RS utilities.
*
* @author Angelo ZERR
*
*/
public class VertxWebUtils {

private VertxWebUtils() {

}

public static PsiAnnotation getRouteAnnotation(PsiElement annotatable) {
return getFirstAnnotation(annotatable, ROUTE_FQN);
}

public static String getRouteAnnotationPath(PsiAnnotation routeAnnotation) {
return getAnnotationMemberValue(routeAnnotation, ROUTE_PATH);
}

/**
* Returns the value of the JAX-RS/Jakarta Path annotation and null otherwise..
*
* @param annotatable the annotatable that might be annotated with the
* JAX-RS/Jakarta Path annotation
* @return the value of the JAX-RS/Jakarta Path annotation and null otherwise..
*/
public static String getJaxRsPathValue(PsiElement annotatable) {
PsiAnnotation annotationPath = getFirstAnnotation(annotatable, JAVAX_WS_RS_PATH_ANNOTATION,
JAKARTA_WS_RS_PATH_ANNOTATION);
if (annotationPath == null) {
return null;
}
return getAnnotationMemberValue(annotationPath, PATH_VALUE);
}

/**
* Returns the value of the JAX-RS/Jakarta ApplicationPath annotation and null
* otherwise.
*
* @param annotatable the annotatable that might be annotated with the
* JAX-RS/Jakarta ApplicationPath annotation
* @return the value of the JAX-RS/Jakarta ApplicationPath annotation and null
* otherwise.
*/
public static String getJaxRsApplicationPathValue(PsiElement annotatable) {
PsiAnnotation annotationApplicationPath = getFirstAnnotation(annotatable, JAVAX_WS_RS_APPLICATIONPATH_ANNOTATION,
JAKARTA_WS_RS_APPLICATIONPATH_ANNOTATION);
if (annotationApplicationPath == null) {
return null;
}
return getAnnotationMemberValue(annotationApplicationPath, PATH_VALUE);
}

/**
* Returns true if the given method has @GET annotation and false otherwise.
*
* @param method the method.
* @return true if the given method has @GET annotation and false otherwise.
*/
public static boolean isClickableJaxRsRequestMethod(PsiMethod method) {
return hasAnyAnnotation(method, JAVAX_WS_RS_GET_ANNOTATION, JAKARTA_WS_RS_GET_ANNOTATION);
}

/**
* Returns true if the given method
* has @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS, or @PATCH annotation
* and false otherwise.
*
* @param method the method.
* @return true if the given method
* has @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS, or @PATCH annotation and
* false otherwise.
*/
public static boolean isJaxRsRequestMethod(PsiMethod method) {
return hasAnyAnnotation(method, HTTP_METHOD_ANNOTATIONS);
}

/**
* Returns an HttpMethod given the FQN of a JAX-RS or Jakarta RESTful
* annotation, nor null if the FQN doesn't match any HttpMethod.
*
* @param annotationFQN the FQN of the annotation to convert into a HttpMethod
* @return an HttpMethod given the FQN of a JAX-RS or Jakarta RESTful
* annotation, nor null if the FQN doesn't match any HttpMethod
*/
public static HttpMethod getHttpMethodForAnnotation(String annotationFQN) {
switch (annotationFQN) {
case JaxRsConstants.JAKARTA_WS_RS_GET_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_GET_ANNOTATION:
return HttpMethod.GET;
case JaxRsConstants.JAKARTA_WS_RS_HEAD_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_HEAD_ANNOTATION:
return HttpMethod.HEAD;
case JaxRsConstants.JAKARTA_WS_RS_POST_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_POST_ANNOTATION:
return HttpMethod.POST;
case JaxRsConstants.JAKARTA_WS_RS_PUT_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_PUT_ANNOTATION:
return HttpMethod.PUT;
case JaxRsConstants.JAKARTA_WS_RS_DELETE_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_DELETE_ANNOTATION:
return HttpMethod.DELETE;
case JaxRsConstants.JAKARTA_WS_RS_PATCH_ANNOTATION:
case JaxRsConstants.JAVAX_WS_RS_PATCH_ANNOTATION:
return HttpMethod.PATCH;
default:
return null;
}
}
}
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@
<projectLabelProvider implementation="com.redhat.microprofile.psi.internal.quarkus.providers.QuarkusProjectLabelProvider"/>
<jaxRsInfoProvider
implementation="com.redhat.microprofile.psi.internal.quarkus.renarde.java.RenardeJaxRsInfoProvider" />
<jaxRsInfoProvider
implementation="com.redhat.microprofile.psi.internal.quarkus.vertx.java.VertxWebJaxRsInfoProvider" />

<!-- Quarkus Core support -->
<propertiesProvider implementation="com.redhat.microprofile.psi.internal.quarkus.core.properties.QuarkusCoreProvider"/>
Expand Down

0 comments on commit 95184a8

Please sign in to comment.