Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Error and documentation overhaul #289

Merged
merged 6 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/main/java/com/hubspot/jinjava/Jinjava.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import com.hubspot.jinjava.interpret.Context;
import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
import com.hubspot.jinjava.interpret.InterpretException;
import com.hubspot.jinjava.interpret.InvalidArgumentException;
import com.hubspot.jinjava.interpret.InvalidInputException;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.RenderResult;
import com.hubspot.jinjava.interpret.TemplateError;
Expand Down Expand Up @@ -203,7 +205,12 @@ public RenderResult renderForResult(String template, Map<String, ?> bindings, Ji
return new RenderResult(TemplateError.fromException((TemplateSyntaxException) e), interpreter.getContext(), interpreter.getErrorsCopy());
}
return new RenderResult(TemplateError.fromSyntaxError(e), interpreter.getContext(), interpreter.getErrorsCopy());
} catch (Exception e) {
} catch (InvalidArgumentException e) {
return new RenderResult(TemplateError.fromInvalidArgumentException(e), interpreter.getContext(), interpreter.getErrorsCopy());
} catch (InvalidInputException e) {
return new RenderResult(TemplateError.fromInvalidInputException(e), interpreter.getContext(), interpreter.getErrorsCopy());
}
catch (Exception e) {
return new RenderResult(TemplateError.fromException(e), interpreter.getContext(), interpreter.getErrorsCopy());
} finally {
globalContext.reset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

String value() default "";

JinjavaParam[] input() default {};

JinjavaParam[] params() default {};

JinjavaSnippet[] snippets() default {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@

String defaultValue() default "";

boolean required() default false;

}
8 changes: 7 additions & 1 deletion src/main/java/com/hubspot/jinjava/el/ExpressionResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@

import com.google.common.collect.ImmutableMap;
import com.hubspot.jinjava.el.ext.NamedParameter;
import com.hubspot.jinjava.interpret.DeferredValueException;
import com.hubspot.jinjava.interpret.DisabledException;
import com.hubspot.jinjava.interpret.InterpretException;
import com.hubspot.jinjava.interpret.InvalidArgumentException;
import com.hubspot.jinjava.interpret.InvalidInputException;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.TemplateError;
import com.hubspot.jinjava.interpret.TemplateError.ErrorItem;
Expand All @@ -23,7 +26,6 @@
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
import com.hubspot.jinjava.interpret.UnknownTokenException;
import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory;
import com.hubspot.jinjava.interpret.DeferredValueException;
import com.hubspot.jinjava.lib.fn.ELFunctionDefinition;

import de.odysseus.el.tree.TreeBuilderException;
Expand Down Expand Up @@ -97,6 +99,10 @@ public Object resolveExpression(String expression) {
} catch (DeferredValueException e) {
// Re-throw so that it can be handled in JinjavaInterpreter
throw e;
} catch (InvalidInputException e) {
interpreter.addError(TemplateError.fromInvalidInputException(e));
} catch (InvalidArgumentException e) {
interpreter.addError(TemplateError.fromInvalidArgumentException(e));
} catch (Exception e) {
interpreter.addError(TemplateError.fromException(new InterpretException(
String.format("Error resolving expression [%s]: " + getRootCauseMessage(e), expression), e, interpreter.getLineNumber(), interpreter.getPosition())));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.hubspot.jinjava.interpret;

import com.hubspot.jinjava.lib.Importable;

public class InvalidArgumentException extends RuntimeException {

private final int lineNumber;
private final int startPosition;
private final String message;
private final String name;

public InvalidArgumentException(JinjavaInterpreter interpreter, Importable importable, InvalidReason invalidReason, int argumentNumber, Object... errorMessageArgs) {
this.message = String.format("Invalid argument in '%s': %s",
importable.getName(),
String.format("%s %s", formatArgumentNumber(argumentNumber + 1), String.format(invalidReason.getErrorMessage(), errorMessageArgs)));

this.lineNumber = interpreter.getLineNumber();
this.startPosition = interpreter.getPosition();
this.name = importable.getName();
}

public String getMessage() {
return message;
}

public int getLineNumber() {
return lineNumber;
}

public int getStartPosition() {
return startPosition;
}

public String getName() {
return name;
}

private static String formatArgumentNumber(int argumentNumber) {

String base = "th";
int remainder = argumentNumber % 10;
if (remainder == 1) {
base = "st";
} else if (remainder == 2) {
base = "nd";
} else if (remainder == 3) {
base = "rd";
}

return String.format("%d%s", argumentNumber, base);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.hubspot.jinjava.interpret;

import com.hubspot.jinjava.lib.Importable;

public class InvalidInputException extends RuntimeException {

private final int lineNumber;
private final int startPosition;
private final String message;
private final String name;

public InvalidInputException(JinjavaInterpreter interpreter, Importable importable, InvalidReason invalidReason, Object... errorMessageArgs) {
this.message = String.format("Invalid input in '%s': input variable %s",
importable.getName(),
String.format(invalidReason.getErrorMessage(), errorMessageArgs));

this.lineNumber = interpreter.getLineNumber();
this.startPosition = interpreter.getPosition();
this.name = importable.getName();
}

public String getMessage() {
return message;
}

public int getLineNumber() {
return lineNumber;
}

public int getStartPosition() {
return startPosition;
}

public String getName() {
return name;
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/hubspot/jinjava/interpret/InvalidReason.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.hubspot.jinjava.interpret;

public enum InvalidReason {
NUMBER_FORMAT("with value '%s' must be a number"),
NULL("cannot be null"),
STRING("must be a string"),
EXPRESSION_TEST("with value %s must be the name of an expression test"),
TEMPORAL_UNIT("with value %s must be a valid temporal unit"),
JSON_READ("could not be converted to an object"),
JSON_WRITE("object could not be written as a string"),
REGEX("with value %s must be valid regex")
;

private final String errorMessage;

InvalidReason(String errorMessage) {
this.errorMessage = errorMessage;
}

public String getErrorMessage() {
return errorMessage;
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/hubspot/jinjava/interpret/TemplateError.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public enum ErrorReason {
EXCEPTION,
MISSING,
DISABLED,
INVALID_ARGUMENT,
INVALID_INPUT,
OTHER
}

Expand Down Expand Up @@ -67,6 +69,28 @@ public static TemplateError fromException(TemplateSyntaxException ex) {
return new TemplateError(ErrorType.FATAL, ErrorReason.SYNTAX_ERROR, ErrorItem.OTHER, ExceptionUtils.getMessage(ex), fieldName, ex.getLineNumber(), ex.getStartPosition(), ex);
}

public static TemplateError fromInvalidArgumentException(InvalidArgumentException ex) {
return new TemplateError(ErrorType.FATAL,
ErrorReason.INVALID_ARGUMENT,
ErrorItem.PROPERTY,
ex.getMessage(),
ex.getName(),
ex.getLineNumber(),
ex.getStartPosition(),
ex);
}

public static TemplateError fromInvalidInputException(InvalidInputException ex) {
return new TemplateError(ErrorType.FATAL,
ErrorReason.INVALID_INPUT,
ErrorItem.PROPERTY,
ex.getMessage(),
ex.getName(),
ex.getLineNumber(),
ex.getStartPosition(),
ex);
}

public static TemplateError fromException(Exception ex) {
int lineNumber = -1;
int startPosition = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@ public class TemplateSyntaxException extends InterpretException {

private final String code;

@Deprecated
public TemplateSyntaxException(String code, String message, int lineNumber, int startPosition) {
super("Syntax error in '" + code + "': " + message, lineNumber, startPosition);
super(String.format("Syntax error in '%s': %s", code, message), lineNumber, startPosition);
this.code = code;
}

@Deprecated
public TemplateSyntaxException(String code, String message, int lineNumber) {
this(code, message, lineNumber, -1);
}

@Deprecated
public TemplateSyntaxException(String code, String message, int lineNumber, int startPosition, Throwable t) {
super(message, t, lineNumber, startPosition);
this.code = code;
}

@Deprecated
public TemplateSyntaxException(String code, String message, int lineNumber, Throwable t) {
this(code, message, lineNumber, -1, t);
}

public TemplateSyntaxException(JinjavaInterpreter interpreter, String code, String message) {
this(code, message, interpreter.getLineNumber(), interpreter.getPosition());
}

public String getCode() {
return code;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

import java.util.Objects;

import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.util.ForLoop;
import com.hubspot.jinjava.util.ObjectIterator;

@JinjavaDoc(
value = "Returns true if a list contains all values in a second list",
input = @JinjavaParam(value = "list", type="list", required = true),
params = @JinjavaParam(value = "list_two", type="list", desc = "The second list to check if every element is in the first list", required = true),
snippets = {
@JinjavaSnippet(
code = "{{ [1, 2, 3] is containingall [2, 3] }}")
})
public class IsContainingAllExpTest implements ExpTest {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

import java.util.Objects;

import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.util.ForLoop;
import com.hubspot.jinjava.util.ObjectIterator;

@JinjavaDoc(
value = "Returns true if a list contains a value",
input = @JinjavaParam(value = "list", type = "list", required = true),
params = @JinjavaParam(value = "value", type = "object", desc = "The value to check is in the list", required = true),
snippets = {
@JinjavaSnippet(
code = "{{ [1, 2, 3] is containing 2 }}")
})
public class IsContainingExpTest implements ExpTest {

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.hubspot.jinjava.lib.exptest;

import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;

@JinjavaDoc(
value = "Return true if the variable is defined",
input = @JinjavaParam(value = "value", type = "object", required = true),
snippets = {
@JinjavaSnippet(
code = "{% if variable is defined %}\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.InterpretException;
import com.hubspot.jinjava.interpret.InvalidArgumentException;
import com.hubspot.jinjava.interpret.InvalidReason;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.TemplateSyntaxException;

@JinjavaDoc(value = "Check if a variable is divisible by a number",
params = {
@JinjavaParam(value = "num", type = "number", desc = "The number to check whether a number is divisble by")
},
@JinjavaDoc(
value = "Returns true if a variable is divisible by a number",
input = @JinjavaParam(value = "num", type = "number", required = true),
params = @JinjavaParam(value = "divisor", type = "number", desc = "The number to check whether a number is divisible by", required = true),
snippets = {
@JinjavaSnippet(
code = "{% if variable is divisbleby 5 %}\n" +
Expand All @@ -34,8 +36,16 @@ public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... ar
return false;
}

if (args.length == 0 || args[0] == null || !Number.class.isAssignableFrom(args[0].getClass())) {
throw new InterpretException(getName() + " test requires a numeric argument");
if (args.length == 0) {
throw new TemplateSyntaxException(interpreter, getName(), "requires 1 argument (name of expression test to filter by)");
}

if (args[0] == null) {
return false;
}

if (!Number.class.isAssignableFrom(args[0].getClass())) {
throw new InvalidArgumentException(interpreter, this, InvalidReason.NUMBER_FORMAT, 0, args[0].toString());
}

return ((Number) var).intValue() % ((Number) args[0]).intValue() == 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.el.TruthyTypeConverter;
import com.hubspot.jinjava.interpret.InterpretException;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.interpret.TemplateSyntaxException;

import de.odysseus.el.misc.BooleanOperations;
import de.odysseus.el.misc.TypeConverter;

@JinjavaDoc(
value = "Check if an object has the same value as another object",
value = "Returns true if an object has the same value as another object",
input = @JinjavaParam(value = "first", type = "object", required = true),
params = {
@JinjavaParam(value = "other", type = "object", desc = "Another object to check equality against")
@JinjavaParam(value = "other", type = "object", desc = "Another object to check equality against", required = true)
},
snippets = {
@JinjavaSnippet(
Expand All @@ -36,7 +37,7 @@ public String getName() {
@Override
public boolean evaluate(Object var, JinjavaInterpreter interpreter, Object... args) {
if (args.length == 0) {
throw new InterpretException(getName() + " test requires 1 argument");
throw new TemplateSyntaxException(interpreter, getName(), "requires 1 argument (other object to check equality against)");
}

return BooleanOperations.eq(TYPE_CONVERTER, var, args[0]);
Expand Down
Loading