/** * Determines if constraint violation occurred in the request entity. If it did, return a client * friendly string representation of where the error occurred (eg. "patient.name") */ public static Optional<String> isRequestEntity(ConstraintViolation<?> violation, Invocable invocable) { final Collection<Path.Node> propertyPath = Lists.of(violation.getPropertyPath()); final Path.Node parent = propertyPath.stream() .skip(1L) .findFirst() .orElse(null); if (parent == null) { return Optional.empty(); } final List<Parameter> parameters = invocable.getParameters(); if (parent.getKind() == ElementKind.PARAMETER) { final Parameter param = parameters.get(parent.as(Path.ParameterNode.class).getParameterIndex()); if (param.getSource().equals(Parameter.Source.UNKNOWN)) { final String path = propertyPath.stream() .skip(2L) .map(Path.Node::toString) .collect(Collectors.joining(".")); return Optional.of(path); } } return Optional.empty(); }
/** * Given a set of constraint violations and a Jersey {@link Invocable} where the constraint * occurred, determine the HTTP Status code for the response. A return value violation is an * internal server error, an invalid request body is unprocessable entity, and any params that * are invalid means a bad request */ public static <T extends ConstraintViolation<?>> int determineStatus(Set<T> violations, Invocable invocable) { if (violations.size() > 0) { final ConstraintViolation<?> violation = violations.iterator().next(); for (Path.Node node : violation.getPropertyPath()) { switch (node.getKind()) { case RETURN_VALUE: return 500; case PARAMETER: // Now determine if the parameter is the request entity final int index = node.as(Path.ParameterNode.class).getParameterIndex(); final Parameter parameter = invocable.getParameters().get(index); return parameter.getSource().equals(Parameter.Source.UNKNOWN) ? 422 : 400; default: continue; } } } // This shouldn't hit, but if it does, we'll return a unprocessable entity return 422; } }
/** * Gets a method parameter (or a parameter field) name, if the violation raised in it. */ private static Optional<String> getMemberName(ConstraintViolation<?> violation, Invocable invocable) { final List<Path.Node> propertyPath = Lists.of(violation.getPropertyPath()); final int size = propertyPath.size(); if (size < 2) { return Optional.empty(); } final Path.Node parent = propertyPath.get(size - 2); final Path.Node member = propertyPath.get(size - 1); switch (parent.getKind()) { case PARAMETER: // Constraint violation most likely failed with a BeanParam final List<Parameter> parameters = invocable.getParameters(); final Parameter param = parameters.get(parent.as(Path.ParameterNode.class).getParameterIndex()); // Extract the failing *Param annotation inside the Bean Param if (param.getSource().equals(Parameter.Source.BEAN_PARAM)) { final Field field = FieldUtils.getField(param.getRawType(), member.getName(), true); return JerseyParameterNameProvider.getParameterNameFromAnnotations(field.getDeclaredAnnotations()); } break; case METHOD: return Optional.of(member.getName()); default: break; } return Optional.empty(); }
private PathExpectation(Path propertyPath) { for ( Path.Node node : propertyPath ) { Integer parameterIndex = null; if ( node.getKind() == ElementKind.PARAMETER ) { parameterIndex = node.as( Path.ParameterNode.class ).getParameterIndex(); } Class<?> containerClass = getContainerClass( node ); Integer typeArgumentIndex = getTypeArgumentIndex( node ); nodes.add( new NodeExpectation( node.getName(), node.getKind(), node.isInIterable(), node.getKey(), node.getIndex(), parameterIndex, containerClass, typeArgumentIndex ) ); } }
private static Annotation[] getAnnotations(ConstraintViolation<?> violation) { // create a simple list of nodes from the path List<Path.Node> nodes = new ArrayList<>(); for (Path.Node node : violation.getPropertyPath()) { nodes.add(node); } Path.Node lastNode = nodes.get(nodes.size() - 1); // the path refers to some property of the leaf bean if (lastNode.getKind() == ElementKind.PROPERTY) { Path.PropertyNode propertyNode = lastNode.as(Path.PropertyNode.class); return getPropertyAnnotations(violation, propertyNode); } // The path refers to a method parameter else if (lastNode.getKind() == ElementKind.PARAMETER && nodes.size() == 2) { Path.MethodNode methodNode = nodes.get(0).as(Path.MethodNode.class); Path.ParameterNode parameterNode = nodes.get(1).as(Path.ParameterNode.class); return getParameterAnnotations(violation, methodNode, parameterNode); } log.warning("Could not read annotations for path: " + violation.getPropertyPath().toString()); return new Annotation[0]; }
private PathExpectation(Path propertyPath) { for ( Path.Node node : propertyPath ) { Integer parameterIndex = null; if ( node.getKind() == ElementKind.PARAMETER ) { parameterIndex = node.as( Path.ParameterNode.class ).getParameterIndex(); } nodes.add( new NodeExpectation( node.getName(), node.getKind(), node.isInIterable(), node.getKey(), node.getIndex(), parameterIndex ) ); } }
/** * Determines if constraint violation occurred in the request entity. If it did, return a client * friendly string representation of where the error occurred (eg. "patient.name") */ public static Optional<String> isRequestEntity(ConstraintViolation<?> violation, Invocable invocable) { final Path.Node parent = Iterables.get(violation.getPropertyPath(), 1, null); if (parent == null) { return Optional.empty(); } final List<Parameter> parameters = invocable.getParameters(); if (parent.getKind() == ElementKind.PARAMETER) { final Parameter param = parameters.get(parent.as(Path.ParameterNode.class).getParameterIndex()); if (param.getSource().equals(Parameter.Source.UNKNOWN)) { return Optional.of(Joiner.on('.').join(Iterables.skip(violation.getPropertyPath(), 2))); } } return Optional.empty(); }
/** * Returns the category for this constraint violation. By default, the category returned * is the full path for property. You can override this method if you prefer another strategy. */ protected String extractCategory(ValuedParameter[] params, ConstraintViolation<Object> violation) { Iterator<Node> path = violation.getPropertyPath().iterator(); Node method = path.next(); logger.debug("Constraint violation on method {}: {}", method, violation); StringBuilder cat = new StringBuilder(); cat.append(params[path.next().as(ParameterNode.class).getParameterIndex()].getName());// parameter name while (path.hasNext()) { cat.append(".").append(path.next()); } return cat.toString(); }
/** * Returns the category for this constraint violation. By default, the category returned * is the name of method with full path for property. You can override this method to * change this behaviour. */ protected String extractCategory(String[] paramNames, ConstraintViolation<Object> violation) { Iterator<Node> path = violation.getPropertyPath().iterator(); StringBuilder cat = new StringBuilder(); cat.append(path.next()).append('.'); // method name cat.append(paramNames[path.next().as(ParameterNode.class).getParameterIndex()]);// parameter name while (path.hasNext()) { cat.append('.').append(path.next()); } return cat.toString(); } }
private static Integer getTypeArgumentIndex(Path.Node node) { Integer typeArgumentIndex = null; if ( node.getKind() == ElementKind.PROPERTY ) { typeArgumentIndex = node.as( Path.PropertyNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.BEAN ) { typeArgumentIndex = node.as( Path.BeanNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { typeArgumentIndex = node.as( Path.ContainerElementNode.class ).getTypeArgumentIndex(); } return typeArgumentIndex; } }
private static Integer getTypeArgumentIndex(Path.Node node) { Integer typeArgumentIndex = null; if ( node.getKind() == ElementKind.PROPERTY ) { typeArgumentIndex = node.as( Path.PropertyNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.BEAN ) { typeArgumentIndex = node.as( Path.BeanNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { typeArgumentIndex = node.as( Path.ContainerElementNode.class ).getTypeArgumentIndex(); } return typeArgumentIndex; } }
private static Class<?> getContainerClass(Path.Node node) { Class<?> containerClass = null; if ( node.getKind() == ElementKind.PROPERTY ) { containerClass = node.as( Path.PropertyNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.BEAN ) { containerClass = node.as( Path.BeanNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { containerClass = node.as( Path.ContainerElementNode.class ).getContainerClass(); } return containerClass; }
private static Class<?> getContainerClass(Path.Node node) { Class<?> containerClass = null; if ( node.getKind() == ElementKind.PROPERTY ) { containerClass = node.as( Path.PropertyNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.BEAN ) { containerClass = node.as( Path.BeanNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { containerClass = node.as( Path.ContainerElementNode.class ).getContainerClass(); } return containerClass; }
Set<ConstraintViolation<Foo>> constraintViolations = ...; Path path = constraintViolations.iterator().next().getPropertyPath(); Iterator<Path.Node> nodeIterator = path.iterator(); Path.Node node = nodeIterator.next(); Bar bar = (Bar) node.as( PropertyNode.class ).getValue();
@Test(expectedExceptions = ClassCastException.class) @SpecAssertion(section = Sections.VALIDATIONAPI_CONSTRAINTVIOLATION, id = "s") public void testPassingWrongTypeToAsOnBeanNodeCausesClassCastException() { Set<ConstraintViolation<VerySpecialClass>> constraintViolations = getValidator().validate( new VerySpecialClass() ); assertNumberOfViolations( constraintViolations, 1 ); ConstraintViolation<VerySpecialClass> constraintViolation = constraintViolations.iterator().next(); Iterator<Path.Node> nodeIter = constraintViolation.getPropertyPath().iterator(); assertTrue( nodeIter.hasNext() ); Node node = nodeIter.next(); assertNode( node, BEAN_NODE_NAME, ElementKind.BEAN, false, null, null ); node.as( PropertyNode.class ); }
if ( leafNode.getKind() == ElementKind.PARAMETER ) { message.append( "\n parameter index: " ); message.append( leafNode.as( Path.ParameterNode.class ).getParameterIndex() );
private PathExpectation(Path propertyPath) { for ( Path.Node node : propertyPath ) { Integer parameterIndex = null; if ( node.getKind() == ElementKind.PARAMETER ) { parameterIndex = node.as( Path.ParameterNode.class ).getParameterIndex(); } Class<?> containerClass = getContainerClass( node ); Integer typeArgumentIndex = getTypeArgumentIndex( node ); nodes.add( new NodeExpectation( node.getName(), node.getKind(), node.isInIterable(), node.getKey(), node.getIndex(), parameterIndex, containerClass, typeArgumentIndex ) ); } }
private static Integer getTypeArgumentIndex(Path.Node node) { Integer typeArgumentIndex = null; if ( node.getKind() == ElementKind.PROPERTY ) { typeArgumentIndex = node.as( Path.PropertyNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.BEAN ) { typeArgumentIndex = node.as( Path.BeanNode.class ).getTypeArgumentIndex(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { typeArgumentIndex = node.as( Path.ContainerElementNode.class ).getTypeArgumentIndex(); } return typeArgumentIndex; } }
private static Class<?> getContainerClass(Path.Node node) { Class<?> containerClass = null; if ( node.getKind() == ElementKind.PROPERTY ) { containerClass = node.as( Path.PropertyNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.BEAN ) { containerClass = node.as( Path.BeanNode.class ).getContainerClass(); } if ( node.getKind() == ElementKind.CONTAINER_ELEMENT ) { containerClass = node.as( Path.ContainerElementNode.class ).getContainerClass(); } return containerClass; }