Allow services parsing to show as warning instead of error

This commit is contained in:
nsingh
2017-01-23 21:26:28 -08:00
parent 5c51b87fb8
commit dfe8cd1e8d
10 changed files with 191 additions and 36 deletions

View File

@@ -0,0 +1,16 @@
/*******************************************************************************
* 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.commons.languageserver.reconcile;
public interface ProblemTypeProvider {
ProblemType getProblemType();
}

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* 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.commons.languageserver.reconcile;
/**
* Exception if there is a failure when parsing a value. It does not wrap
* other exceptions such that when thrown, the parse exception is the "deepest"
* error.
*
*/
public class ReconcileException extends Exception implements ProblemTypeProvider {
/**
*
*/
private static final long serialVersionUID = 1L;
private final ProblemType problemType;
public ReconcileException(String message, ProblemType problemType) {
super(message);
this.problemType = problemType;
}
@Override
public ProblemType getProblemType() {
return problemType;
}
}

View File

@@ -48,7 +48,7 @@ public class EnumValueParser implements ValueParser {
// IMPORTANT: check the text FIRST before fetching values
// from the hints provider, as the hints provider may be expensive when resolving values
if (!StringUtil.hasText(str)) {
throw toValueParseError(createBlankTextErrorMessage());
throw createException(createBlankTextErrorMessage());
}
Collection<String> values = this.values.get();
@@ -57,12 +57,13 @@ public class EnumValueParser implements ValueParser {
if (values==null || values.contains(str)) {
return str;
} else {
throw toValueParseError(createErrorMessage(str, values));
throw createException(createErrorMessage(str, values));
}
}
protected Exception toValueParseError(String message) throws Exception{
return ExceptionUtil.asValueParseException(message);
protected Exception createException(String message) {
return new IllegalArgumentException(message);
}
protected String createBlankTextErrorMessage() {

View File

@@ -61,15 +61,7 @@ public class ExceptionUtil {
// The message of nested exception is usually more interesting than the
// one on top.
Throwable cause = getDeepestCause(e);
// If value parse exception, do not append any additional information
if (cause instanceof ValueParseException) {
String msg = cause.getMessage();
if (StringUtil.hasText(msg)) {
return msg;
} else {
return "An error occurred: " + getSimpleError(cause);
}
} else if (cause != null) {
if (cause != null) {
String msg = getSimpleError(cause) + ": " + cause.getMessage();
return msg;
} else {
@@ -77,7 +69,7 @@ public class ExceptionUtil {
}
}
private static String getSimpleError(Throwable e) {
public static String getSimpleError(Throwable e) {
return e.getClass().getSimpleName();
}
@@ -135,8 +127,4 @@ public class ExceptionUtil {
return exception(error);
}
}
public static Exception asValueParseException(String message) {
return new ValueParseException(message);
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.yaml.reconcile;
import static org.springframework.ide.vscode.commons.util.ExceptionUtil.getSimpleError;
import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar;
import java.util.ArrayList;
@@ -21,11 +22,12 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTypeProvider;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.util.CollectionUtil;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.IntegerRange;
import org.springframework.ide.vscode.commons.util.Log;
@@ -160,8 +162,9 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
parser.parse(value);
}
} catch (Exception e) {
String msg = ExceptionUtil.getMessage(e);
valueParseError(type, node, msg);
ProblemType problemType = getProblemType(e);
String msg = getMessage(e);
valueParseError(type, node, msg, problemType);
}
}
} else {
@@ -174,6 +177,27 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
}
}
private String getMessage(Exception _e) {
Throwable e = ExceptionUtil.getDeepestCause(_e);
// If value parse exception, do not append any additional information
if (e instanceof ReconcileException) {
String msg = e.getMessage();
if (StringUtil.hasText(msg)) {
return msg;
} else {
return "An error occurred: " + getSimpleError(e);
}
} else {
return ExceptionUtil.getMessage(e);
}
}
protected ProblemType getProblemType(Exception _e) {
Throwable e = ExceptionUtil.getDeepestCause(_e);
return e instanceof ProblemTypeProvider ? ((ProblemTypeProvider) e).getProblemType() : YamlSchemaProblems.SCHEMA_PROBLEM;
}
private void checkRequiredProperties(MappingNode map, YType type, Map<String, YTypedProperty> beanProperties) {
Set<String> foundProps = NodeUtil.getScalarKeys(map);
boolean allPropertiesKnown = beanProperties.keySet().containsAll(foundProps);
@@ -271,12 +295,12 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
}
}
}
private void valueParseError(YType type, Node node, String parseErrorMsg) {
private void valueParseError(YType type, Node node, String parseErrorMsg, ProblemType problemType) {
if (!StringUtil.hasText(parseErrorMsg)) {
parseErrorMsg= "Couldn't parse as '"+describe(type)+"'";
}
problem(node, parseErrorMsg);
problem(node, parseErrorMsg, problemType);
}
private void unknownBeanProperty(Node keyNode, YType type, String name) {
@@ -331,6 +355,10 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
private void problem(Node node, String msg) {
problems.accept(YamlSchemaProblems.schemaProblem(msg, node));
}
private void problem(Node node, String msg, ProblemType problemType) {
problems.accept(YamlSchemaProblems.problem(msg, node, problemType));
}
private void problem(DocumentRegion region, String msg) {
problems.accept(YamlSchemaProblems.schemaProblem(msg, region));

View File

@@ -16,7 +16,6 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.yaml.snakeyaml.nodes.Node;
/**
@@ -26,10 +25,11 @@ import org.yaml.snakeyaml.nodes.Node;
*/
public class YamlSchemaProblems {
private static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem");
private static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem");
public static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem");
public static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem");
private static ProblemType problemType(final String typeName) {
public static ProblemType problemType(final String typeName, ProblemSeverity defaultSeverity) {
return new ProblemType() {
@Override
public String toString() {
@@ -37,7 +37,7 @@ public class YamlSchemaProblems {
}
@Override
public ProblemSeverity getDefaultSeverity() {
return ProblemSeverity.ERROR;
return defaultSeverity;
}
@Override
public String getCode() {
@@ -45,6 +45,10 @@ public class YamlSchemaProblems {
}
};
}
public static ProblemType problemType(final String typeName) {
return problemType(typeName, ProblemSeverity.ERROR);
}
public static ReconcileProblem syntaxProblem(String msg, int offset, int len) {
return new ReconcileProblemImpl(SYNTAX_PROBLEM, msg, offset, len);
@@ -59,5 +63,10 @@ public class YamlSchemaProblems {
public static ReconcileProblem schemaProblem(String msg, DocumentRegion node) {
return new ReconcileProblemImpl(SCHEMA_PROBLEM, msg, node.getStart(), node.getLength());
}
public static ReconcileProblem problem(String msg, Node node, ProblemType problemType) {
int start = node.getStartMark().getIndex();
int end = node.getEndMark().getIndex();
return new ReconcileProblemImpl(problemType, msg, start, end-start);
}
}

View File

@@ -23,6 +23,7 @@ import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTar
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.ValueParseException;
import org.springframework.ide.vscode.commons.yaml.schema.YValueHint;
public abstract class AbstractCFHintsProvider implements Callable<Collection<YValueHint>> {
@@ -57,15 +58,14 @@ public abstract class AbstractCFHintsProvider implements Callable<Collection<YVa
logger.log(Level.SEVERE, e.getMessage(), e);
if (ExceptionUtil.getThrowable(e, IOException.class) != null) {
throw ExceptionUtil.asValueParseException(
throw new ValueParseException(
"Connection failure to Cloud Foundry. Please check the log for more details.");
} else {
throw ExceptionUtil.asValueParseException(
throw new ValueParseException(
"Failed to fetch values from Cloud Foundry. Please check the log for more details.");
}
}
else {
} else {
throw e;
}
}

View File

@@ -13,6 +13,7 @@ package org.springframework.ide.vscode.manifest.yaml;
import java.util.Collection;
import java.util.concurrent.Callable;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileException;
import org.springframework.ide.vscode.commons.util.EnumValueParser;
public class CFServicesValueParser extends EnumValueParser {
@@ -21,12 +22,18 @@ public class CFServicesValueParser extends EnumValueParser {
super(typeName, values);
}
@Override
protected String createErrorMessage(String parseString, Collection<String> values) {
return "There is no service instance called '" + parseString + "'. Available service instances are: " + values;
}
@Override
protected String createBlankTextErrorMessage() {
return "At least one service instance name must be specified";
}
@Override
protected Exception createException(String message) {
return new ReconcileException(message, ManifestYamlSchemaProblemsTypes.UNKNOWN_SERVICES_PROBLEM);
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* 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.manifest.yaml;
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.problemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
/**
*
*/
public class ManifestYamlSchemaProblemsTypes {
public static final ProblemType UNKNOWN_SERVICES_PROBLEM = problemType("UnknownServicesProblem",
ProblemSeverity.WARNING);
}

View File

@@ -10,20 +10,27 @@
*******************************************************************************/
package org.springframework.ide.vscode.manifest.yaml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import java.io.IOException;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance;
import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests;
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CfCliParamsProvider;
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.ClientParamsProvider;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.languageserver.testharness.Editor;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import com.google.common.collect.ImmutableList;
public class ManifestYamlEditorTest {
LanguageServerHarness harness;
@@ -782,6 +789,42 @@ public class ManifestYamlEditorTest {
);
editor.assertProblems("bogus|Unknown property");
}
@Test
public void reconcileShowsWarningOnUnknownService() throws Exception {
ClientRequests cfClient = cfClientFactory.client;
CFServiceInstance service = Mockito.mock(CFServiceInstance.class);
when(service.getName()).thenReturn("myservice");
when(cfClient.getServices()).thenReturn(ImmutableList.of());
Editor editor = harness.newEditor(
"applications:\n" +
"- name: foo\n" +
" services:\n" +
" - bad-service\n"
);
editor.assertProblems("bad-service|There is no service instance called");
Diagnostic problem = editor.assertProblem("bad-service");
assertEquals(DiagnosticSeverity.Warning, problem.getSeverity());
}
@Test
public void reconcileShowsWarningOnNoService() throws Exception {
ClientRequests cfClient = cfClientFactory.client;
when(cfClient.getServices()).thenReturn(ImmutableList.of());
Editor editor = harness.newEditor(
"applications:\n" +
"- name: foo\n" +
" services:\n" +
" - bad-service\n");
editor.assertProblems("bad-service|There is no service instance called");
Diagnostic problem = editor.assertProblem("bad-service");
assertEquals(DiagnosticSeverity.Warning, problem.getSeverity());
}
//////////////////////////////////////////////////////////////////////////////