Skip to content

Commit

Permalink
More checks for return types from GraphQL operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartisk committed Nov 28, 2023
1 parent 5aee8ef commit aaae4ee
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 7 deletions.
5 changes: 5 additions & 0 deletions projects/lsp4mp/projects/maven/microprofile-graphql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<artifactId>smallrye-graphql-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>mutiny</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-graphql-client-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

package io.openliberty.graphql.sample;

import io.smallrye.graphql.api.Subscription;

import java.util.concurrent.Flow;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
Expand Down Expand Up @@ -95,4 +98,14 @@ public void myMethod() {
public void myOtherMethod() {
}

@Subscription
public String subscriptionReturningNonMulti() {
return null;
}

@Query
public Flow.Publisher<String> queryReturningMulti() {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ private MicroProfileGraphQLConstants() {

public static final String QUERY_ANNOTATION = "org.eclipse.microprofile.graphql.Query";
public static final String MUTATION_ANNOTATION = "org.eclipse.microprofile.graphql.Mutation";
public static final String SUBSCRIPTION_ANNOTATION = "org.eclipse.microprofile.graphql.Subscription";
public static final String SUBSCRIPTION_ANNOTATION = "io.smallrye.graphql.api.Subscription";
public static final String GRAPHQL_API_ANNOTATION = "org.eclipse.microprofile.graphql.GraphQLApi";
public static final String UNION_ANNOTATION = "io.smallrye.graphql.api.Union";
public static final String DIRECTIVE_ANNOTATION = "io.smallrye.graphql.api.Directive";
public static final String DIAGNOSTIC_SOURCE = "microprofile-graphql";
public static final String MULTI = "io.smallrye.mutiny.Multi";
public static final String FLOW_PUBLISHER = "java.util.concurrent.Flow.Publisher";

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class MicroProfileGraphQLASTValidator extends JavaASTValidator {
private static final String NO_VOID_MESSAGE = "Methods annotated with microprofile-graphql's `@Query` cannot have 'void' as a return type.";
private static final String NO_VOID_MUTATION_MESSAGE = "Methods annotated with microprofile-graphql's `@Mutation` cannot have 'void' as a return type.";
private static final String WRONG_DIRECTIVE_PLACEMENT = "Directive ''{0}'' is not allowed on element type ''{1}''";
private static final String SUBSCRIPTION_MUST_RETURN_MULTI = "Methods annotated with `@Subscription` have to return either `io.smallrye.mutiny.Multi` or `java.util.concurrent.Flow.Publisher`.";
private static final String SINGLE_RESULT_OPERATION_MUST_NOT_RETURN_MULTI = "Methods annotated with `@Query` or `@Mutation` cannot return `io.smallrye.mutiny.Multi` or `java.util.concurrent.Flow.Publisher`.";

boolean allowsVoidReturnFromOperations = true;

Expand All @@ -84,6 +86,8 @@ public void visitMethod(PsiMethod node) {
if(!allowsVoidReturnFromOperations) {
validateNoVoidReturnedFromOperations(node);
}
validateMultiReturnTypeFromSubscriptions(node);
validateNoMultiReturnTypeFromQueriesAndMutations(node);
super.visitMethod(node);
}

Expand Down Expand Up @@ -216,4 +220,63 @@ private void validateNoVoidReturnedFromOperations(PsiMethod node) {
}
}

/**
* A method annotated with `@Subscription` must return a `Multi` or `Flow.Publisher`.
*/
private void validateMultiReturnTypeFromSubscriptions(PsiMethod node) {
if(node.getReturnType() == null) {
return;
}
for (PsiAnnotation annotation : node.getAnnotations()) {
if (isMatchAnnotation(annotation, MicroProfileGraphQLConstants.SUBSCRIPTION_ANNOTATION)) {
if(node.getReturnType().equals(PsiType.VOID)) {
super.addDiagnostic(SUBSCRIPTION_MUST_RETURN_MULTI,
MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
node.getReturnTypeElement(),
MicroProfileGraphQLErrorCode.SUBSCRIPTION_MUST_RETURN_MULTI,
DiagnosticSeverity.Error);
return;
}
if (node.getReturnType() instanceof PsiClassReferenceType) {
String returnTypeName = ((PsiClassReferenceType) node.getReturnType()).getReference().getQualifiedName();
if (!returnTypeName.equals(MicroProfileGraphQLConstants.MULTI)
&& !returnTypeName.equals(MicroProfileGraphQLConstants.FLOW_PUBLISHER)) {
super.addDiagnostic(SUBSCRIPTION_MUST_RETURN_MULTI,
MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
node.getReturnTypeElement(),
MicroProfileGraphQLErrorCode.SUBSCRIPTION_MUST_RETURN_MULTI,
DiagnosticSeverity.Error);
}

}
}
}
}

/**
* Methods annotated with `@Query` or `@Mutation` must NOT return
* a `Multi` or `Flow.Publisher` type.
*/
private void validateNoMultiReturnTypeFromQueriesAndMutations(PsiMethod node) {
if(node.getReturnType() == null) {
return;
}
for (PsiAnnotation annotation : node.getAnnotations()) {
if (isMatchAnnotation(annotation, MicroProfileGraphQLConstants.QUERY_ANNOTATION)
|| isMatchAnnotation(annotation, MicroProfileGraphQLConstants.MUTATION_ANNOTATION)) {
if (node.getReturnType() instanceof PsiClassReferenceType) {
String returnTypeName = ((PsiClassReferenceType) node.getReturnType()).getReference().getQualifiedName();
if (returnTypeName.equals(MicroProfileGraphQLConstants.MULTI)
|| returnTypeName.equals(MicroProfileGraphQLConstants.FLOW_PUBLISHER)) {
super.addDiagnostic(SINGLE_RESULT_OPERATION_MUST_NOT_RETURN_MULTI,
MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
node.getReturnTypeElement(),
MicroProfileGraphQLErrorCode.SINGLE_RESULT_OPERATION_MUST_NOT_RETURN_MULTI,
DiagnosticSeverity.Error);
}
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ public enum MicroProfileGraphQLErrorCode implements IJavaErrorCode {

NO_VOID_QUERIES,
NO_VOID_MUTATIONS,
WRONG_DIRECTIVE_PLACEMENT
;
WRONG_DIRECTIVE_PLACEMENT,
SUBSCRIPTION_MUST_RETURN_MULTI,
SINGLE_RESULT_OPERATION_MUST_NOT_RETURN_MULTI;

@Override
public String getCode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,33 @@ public void testIncorrectDirectivePlacement() throws Exception {
diagnosticsParams.setUris(Collections.singletonList(javaFileUri));
diagnosticsParams.setDocumentFormat(DocumentFormat.Markdown);

Diagnostic d1 = d(38, 4, 15,
Diagnostic d1 = d(41, 4, 15,
"Directive 'io.openliberty.graphql.sample.Optimistic' is not allowed on element type 'FIELD_DEFINITION'",
DiagnosticSeverity.Error, MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
MicroProfileGraphQLErrorCode.WRONG_DIRECTIVE_PLACEMENT);

Diagnostic d2 = d(48, 50, 61,
Diagnostic d2 = d(51, 50, 61,
"Directive 'io.openliberty.graphql.sample.Optimistic' is not allowed on element type 'ARGUMENT_DEFINITION'",
DiagnosticSeverity.Error, MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
MicroProfileGraphQLErrorCode.WRONG_DIRECTIVE_PLACEMENT);

Diagnostic d3 = d(72, 59, 70,
Diagnostic d3 = d(75, 59, 70,
"Directive 'io.openliberty.graphql.sample.Optimistic' is not allowed on element type 'ARGUMENT_DEFINITION'",
DiagnosticSeverity.Error, MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
MicroProfileGraphQLErrorCode.WRONG_DIRECTIVE_PLACEMENT);

Diagnostic d4 = d(101, 11, 17,
"Methods annotated with `@Subscription` have to return either `io.smallrye.mutiny.Multi` or `java.util.concurrent.Flow.Publisher`.",
DiagnosticSeverity.Error, MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
MicroProfileGraphQLErrorCode.SUBSCRIPTION_MUST_RETURN_MULTI);

Diagnostic d5 = d(106, 11, 33,
"Methods annotated with `@Query` or `@Mutation` cannot return `io.smallrye.mutiny.Multi` or `java.util.concurrent.Flow.Publisher`.",
DiagnosticSeverity.Error, MicroProfileGraphQLConstants.DIAGNOSTIC_SOURCE,
MicroProfileGraphQLErrorCode.SINGLE_RESULT_OPERATION_MUST_NOT_RETURN_MULTI);

assertJavaDiagnostics(diagnosticsParams, utils,
d1, d2, d3);
d1, d2, d3, d4, d5);
}

}

0 comments on commit aaae4ee

Please sign in to comment.