Skip to content

Commit

Permalink
feat: add support for @startup healthcheck diagnostic
Browse files Browse the repository at this point in the history
Signed-off-by: Fred Bricon <fbricon@gmail.com>
  • Loading branch information
fbricon authored and angelozerr committed May 3, 2024
1 parent 017b77b commit 83866ea
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ private MicroProfileHealthConstants() {
public static final String HEALTH_ANNOTATION = "org.eclipse.microprofile.health.Health";
public static final String READINESS_ANNOTATION = "org.eclipse.microprofile.health.Readiness";
public static final String LIVENESS_ANNOTATION = "org.eclipse.microprofile.health.Liveness";
public static final String STARTUP_ANNOTATION = "org.eclipse.microprofile.health.Startup";

public static final String HEALTH_CHECK_INTERFACE_NAME = "HealthCheck";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
public class HealthAnnotationMissingQuickFix extends InsertAnnotationMissingQuickFix {

public HealthAnnotationMissingQuickFix() {
super(MicroProfileHealthConstants.LIVENESS_ANNOTATION, MicroProfileHealthConstants.READINESS_ANNOTATION,
super(MicroProfileHealthConstants.LIVENESS_ANNOTATION, MicroProfileHealthConstants.READINESS_ANNOTATION,MicroProfileHealthConstants.STARTUP_ANNOTATION,
MicroProfileHealthConstants.HEALTH_ANNOTATION);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,24 @@
import org.eclipse.lsp4mp.commons.DocumentFormat;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.HEALTH_ANNOTATION;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.HEALTH_CHECK_INTERFACE;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.HEALTH_CHECK_INTERFACE_NAME;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.LIVENESS_ANNOTATION;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.READINESS_ANNOTATION;
import static com.redhat.devtools.intellij.lsp4mp4ij.psi.internal.health.MicroProfileHealthConstants.*;

/**
*
* MicroProfile Health Diagnostics:
*
* <ul>
* <li>Diagnostic 1:display Health annotation diagnostic message if
* Health/Liveness/Readiness annotation exists but HealthCheck interface is not
* Health/Liveness/Readiness/Startup annotation exists but HealthCheck interface is not
* implemented</li>
* <li>Diagnostic 2: display HealthCheck diagnostic message if HealthCheck
* interface is implemented but Health/Liveness/Readiness annotation does not
* interface is implemented but Health/Liveness/Readiness/Startup annotation does not
* exist</li>
*
* </ul>
Expand Down Expand Up @@ -83,12 +80,10 @@ public List<Diagnostic> collectDiagnostics(JavaDiagnosticsContext context) {
private static void collectDiagnostics(PsiElement[] elements, List<Diagnostic> diagnostics,
JavaDiagnosticsContext context) {
for (PsiElement element : elements) {
if (element instanceof PsiClass) {
PsiClass type = (PsiClass) element;
if (element instanceof PsiClass type) {
if (!type.isInterface()) {
validateClassType(type, diagnostics, context);
}
continue;
}
}
}
Expand All @@ -100,9 +95,9 @@ private static void validateClassType(PsiClass classType, List<Diagnostic> diagn
PsiClass[] interfaces = findImplementedInterfaces(classType);
boolean implementsHealthCheck = Stream.of(interfaces)
.anyMatch(interfaceType -> HEALTH_CHECK_INTERFACE_NAME.equals(interfaceType.getName()));
boolean hasOneOfHealthAnnotation = AnnotationUtils.hasAnyAnnotation(classType, LIVENESS_ANNOTATION, READINESS_ANNOTATION, HEALTH_ANNOTATION);
boolean hasOneOfHealthAnnotation = AnnotationUtils.hasAnyAnnotation(classType, LIVENESS_ANNOTATION, READINESS_ANNOTATION, STARTUP_ANNOTATION, HEALTH_ANNOTATION);
// Diagnostic 1:display Health annotation diagnostic message if
// Health/Liveness/Readiness annotation exists but HealthCheck interface is not
// Health/Liveness/Readiness/Startup annotation exists but HealthCheck interface is not
// implemented
if (hasOneOfHealthAnnotation && !implementsHealthCheck) {
Range healthCheckInterfaceRange = PositionUtils.toNameRange(classType, utils);
Expand All @@ -113,7 +108,7 @@ private static void validateClassType(PsiClass classType, List<Diagnostic> diagn
}

// Diagnostic 2: display HealthCheck diagnostic message if HealthCheck interface
// is implemented but Health/Liveness/Readiness annotation does not exist
// is implemented but Health/Liveness/Readiness/Startup annotation does not exist
if (implementsHealthCheck && !hasOneOfHealthAnnotation) {
Range healthCheckInterfaceRange = PositionUtils.toNameRange(classType, utils);
Diagnostic d = context.createDiagnostic(uri, createDiagnostic2Message(classType, documentFormat),
Expand All @@ -124,52 +119,52 @@ private static void validateClassType(PsiClass classType, List<Diagnostic> diagn
}

private static String createDiagnostic1Message(PsiClass classType, DocumentFormat documentFormat) {
StringBuilder message = new StringBuilder("The class ");
if (DocumentFormat.Markdown.equals(documentFormat)) {
message.append("`");
}
message.append(classType.getQualifiedName());
if (DocumentFormat.Markdown.equals(documentFormat)) {
message.append("`");
}
message.append(" using the @Liveness");
boolean hasHealth = PsiTypeUtils.findType(classType.getManager(), HEALTH_ANNOTATION) != null;
if (!hasHealth) {
message.append(" or ");
} else {
message.append(", ");
}
message.append("@Readiness");
if (hasHealth) {
message.append(", or @Health");
}
StringBuilder message = getMessage(classType, documentFormat);
message.append(" using the ");
listAvailableAnnotations(classType, message);
message.append(" annotation should implement the HealthCheck interface.");
return message.toString();
}

private static String createDiagnostic2Message(PsiClass classType, DocumentFormat documentFormat) {
StringBuilder message = getMessage(classType, documentFormat);
message.append(" implementing the HealthCheck interface should use the ");
listAvailableAnnotations(classType, message);
message.append(" annotation.");
return message.toString();
}

private static @NotNull StringBuilder getMessage(PsiClass classType, DocumentFormat documentFormat) {
StringBuilder message = new StringBuilder("The class ");
if (DocumentFormat.Markdown.equals(documentFormat)) {
message.append("`");
}
message.append(classType.getQualifiedName());
if (DocumentFormat.Markdown.equals(documentFormat)) {
message.append("`");
String backtick = (documentFormat == DocumentFormat.Markdown) ? "`" : "";
message.append(backtick)
.append(classType.getQualifiedName())
.append(backtick);
return message;
}

private static void listAvailableAnnotations(PsiClass classType, StringBuilder message) {
List<String> annotations = new ArrayList<>(4);
annotations.add("@Liveness");
annotations.add("@Readiness");
if (PsiTypeUtils.findType(classType.getManager(), STARTUP_ANNOTATION) != null) {
annotations.add("@Startup");
}
message.append(
" implementing the HealthCheck interface should use the @Liveness");
boolean hasHealth = PsiTypeUtils.findType(classType.getManager(), HEALTH_ANNOTATION) != null;
if (!hasHealth) {
message.append(" or ");
} else {
message.append(", ");
;
if (PsiTypeUtils.findType(classType.getManager(), HEALTH_ANNOTATION) != null) {
annotations.add("@Health");
}
message.append("@Readiness");
if (hasHealth) {
message.append(", or @Health");
;
int size = annotations.size();
int secondLast = size - 2;
for(int i = 0; i < size; i++) {
message.append(annotations.get(i));
if (i == secondLast) {
message.append(" or ");
} else if (i < secondLast){
message.append(", ");
}
}
message.append(" annotation.");
return message.toString();
}

private static PsiClass[] findImplementedInterfaces(PsiClass type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void testImplementHealthCheck() throws Exception {
diagnosticsParams.setDocumentFormat(DocumentFormat.Markdown);

Diagnostic d = d(9, 13, 37,
"The class `org.acme.health.DontImplementHealthCheck` using the @Liveness, @Readiness, or @Health annotation should implement the HealthCheck interface.",
"The class `org.acme.health.DontImplementHealthCheck` using the @Liveness, @Readiness or @Health annotation should implement the HealthCheck interface.",
DiagnosticSeverity.Warning, MicroProfileHealthConstants.DIAGNOSTIC_SOURCE,
MicroProfileHealthErrorCode.ImplementHealthCheck);
assertJavaDiagnostics(diagnosticsParams, PsiUtilsLSImpl.getInstance(myProject), //
Expand Down Expand Up @@ -78,7 +78,7 @@ public void testHealthAnnotationMissing() throws Exception {
diagnosticsParams.setDocumentFormat(DocumentFormat.Markdown);

Diagnostic d = d(5, 13, 33,
"The class `org.acme.health.ImplementHealthCheck` implementing the HealthCheck interface should use the @Liveness, @Readiness, or @Health annotation.",
"The class `org.acme.health.ImplementHealthCheck` implementing the HealthCheck interface should use the @Liveness, @Readiness or @Health annotation.",
DiagnosticSeverity.Warning, MicroProfileHealthConstants.DIAGNOSTIC_SOURCE,
MicroProfileHealthErrorCode.HealthAnnotationMissing);
assertJavaDiagnostics(diagnosticsParams, PsiUtilsLSImpl.getInstance(myProject), //
Expand Down Expand Up @@ -118,7 +118,7 @@ public void testHealthAnnotationMissingv3() throws Exception {
diagnosticsParams.setDocumentFormat(DocumentFormat.Markdown);

Diagnostic d = d(5, 13, 28,
"The class `org.acme.MyLivenessCheck` implementing the HealthCheck interface should use the @Liveness or @Readiness annotation.",
"The class `org.acme.MyLivenessCheck` implementing the HealthCheck interface should use the @Liveness, @Readiness or @Startup annotation.",
DiagnosticSeverity.Warning, MicroProfileHealthConstants.DIAGNOSTIC_SOURCE,
MicroProfileHealthErrorCode.HealthAnnotationMissing);
assertJavaDiagnostics(diagnosticsParams, utils, //
Expand All @@ -130,7 +130,9 @@ public void testHealthAnnotationMissingv3() throws Exception {
ca(uri, "Insert @Liveness", MicroProfileCodeActionId.InsertMissingHealthAnnotation, d, //
te(0, 0, 13, 0, "package org.acme;\n\nimport org.eclipse.microprofile.health.HealthCheck;\nimport org.eclipse.microprofile.health.HealthCheckResponse;\nimport org.eclipse.microprofile.health.Liveness;\n\n@Liveness\npublic class MyLivenessCheck implements HealthCheck {\n\n @Override\n public HealthCheckResponse call() {\n return HealthCheckResponse.up(\"alive\");\n }\n\n}\n")), //
ca(uri, "Insert @Readiness", MicroProfileCodeActionId.InsertMissingHealthAnnotation, d, //
te(0, 0, 13, 0, "package org.acme;\n\nimport org.eclipse.microprofile.health.HealthCheck;\nimport org.eclipse.microprofile.health.HealthCheckResponse;\nimport org.eclipse.microprofile.health.Readiness;\n\n@Readiness\npublic class MyLivenessCheck implements HealthCheck {\n\n @Override\n public HealthCheckResponse call() {\n return HealthCheckResponse.up(\"alive\");\n }\n\n}\n"))
te(0, 0, 13, 0, "package org.acme;\n\nimport org.eclipse.microprofile.health.HealthCheck;\nimport org.eclipse.microprofile.health.HealthCheckResponse;\nimport org.eclipse.microprofile.health.Readiness;\n\n@Readiness\npublic class MyLivenessCheck implements HealthCheck {\n\n @Override\n public HealthCheckResponse call() {\n return HealthCheckResponse.up(\"alive\");\n }\n\n}\n")),//
ca(uri, "Insert @Startup", MicroProfileCodeActionId.InsertMissingHealthAnnotation, d, //
te(0, 0, 13, 0, "package org.acme;\n\nimport org.eclipse.microprofile.health.HealthCheck;\nimport org.eclipse.microprofile.health.HealthCheckResponse;\nimport org.eclipse.microprofile.health.Startup;\n\n@Startup\npublic class MyLivenessCheck implements HealthCheck {\n\n @Override\n public HealthCheckResponse call() {\n return HealthCheckResponse.up(\"alive\");\n }\n\n}\n"))
);
}
}

0 comments on commit 83866ea

Please sign in to comment.