All Downloads are FREE. Search and download functionalities are using the official Maven repository.

graphql.validation.interpolation.ResourceBundleMessageInterpolator Maven / Gradle / Ivy

package graphql.validation.interpolation;

import graphql.ErrorClassification;
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import graphql.execution.ResultPath;
import graphql.schema.GraphQLDirective;
import graphql.validation.el.StandardELVariables;
import graphql.validation.rules.ValidationEnvironment;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Optional;
import java.util.ResourceBundle;
import jakarta.validation.Constraint;
import jakarta.validation.Path;
import jakarta.validation.Payload;
import org.hibernate.validator.internal.engine.MessageInterpolatorContext;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl.ConstraintType;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
import org.hibernate.validator.messageinterpolation.ExpressionLanguageFeatureLevel;
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * This message interpolator will try to convert message templates into I18N messages and then run message property replacement
 * and expression interpolation.
 * 

* * By default this looks for a resource bundle file called "ValidationMessages.properties" on the class path but you can can * override {@link #getResourceBundle(java.util.Locale)} to provide your own resource bundle *

* If it finds no resources then it will use the message template as is and do parameter and expression replacement * on it *

* This class is heavily inspired by the Hibernate Validator projects ResourceBundleMessageInterpolator implementation and in fact * uses that in its implementation and hence the standard facilities such as well known "parameters" like "validatedValue" and "format" * are available. *

* See the Hibernate Validation documentation for more details */ public class ResourceBundleMessageInterpolator implements MessageInterpolator { private ResourceBundleLocator userResourceBundleLocator = new PlatformResourceBundleLocator("ValidationMessages"); private ResourceBundleLocator systemResourceBundleLocator = new PlatformResourceBundleLocator("graphql.validation.ValidationMessages"); private Locale defaultLocale = Locale.getDefault(); /** * Override this method to build your own ErrorClassification * * @param messageTemplate the message template * @param messageParams the parameters * @param validationEnvironment the rule environment * * @return an ErrorClassification */ @SuppressWarnings("unused") protected ErrorClassification buildErrorClassification(String messageTemplate, Map messageParams, ValidationEnvironment validationEnvironment) { ResultPath fieldOrArgumentPath = validationEnvironment.getValidatedPath(); GraphQLDirective directive = validationEnvironment.getContextObject(GraphQLDirective.class); return new ValidationErrorType(fieldOrArgumentPath, directive); } /** * You can override this to provide your own resource bundles for a given locale * * @param locale the locale in question * * @return a resource bundle OR null if you don't have one */ @SuppressWarnings("unused") protected ResourceBundle getResourceBundle(Locale locale) { return null; } @Override public GraphQLError interpolate(String messageTemplate, Map messageParams, ValidationEnvironment validationEnvironment) { ErrorClassification errorClassification = buildErrorClassification(messageTemplate, messageParams, validationEnvironment); String message = interpolateMessageImpl(messageTemplate, messageParams, validationEnvironment); GraphqlErrorBuilder errorBuilder = GraphqlErrorBuilder.newError() .message(message) .errorType(errorClassification) .path(validationEnvironment.getExecutionPath()); if (validationEnvironment.getLocation() != null) { errorBuilder.location(validationEnvironment.getLocation()); } return errorBuilder.build(); } private String interpolateMessageImpl(String messageTemplate, Map messageParams, ValidationEnvironment validationEnvironment) { Locale locale = validationEnvironment.getLocale() == null ? defaultLocale : validationEnvironment.getLocale(); messageTemplate = loadMessageResource(messageTemplate, locale); MessageInterpolatorContext context = buildHibernateContext(messageParams, validationEnvironment); if (locale == null) { // let hibernate code do the local defaulting return hibernateInterpolator().interpolate(messageTemplate, context); } else { return hibernateInterpolator().interpolate(messageTemplate, context, locale); } } private String loadMessageResource(String messageTemplate, Locale locale) { ResourceBundle resourceBundle = getResourceBundle(locale); Optional bundleMessage = loadMessageFromBundle(messageTemplate, resourceBundle); if (!bundleMessage.isPresent()) { bundleMessage = loadMessageFromBundle(messageTemplate, userResourceBundleLocator.getResourceBundle(locale)); if (!bundleMessage.isPresent()) { bundleMessage = loadMessageFromBundle(messageTemplate, systemResourceBundleLocator.getResourceBundle(locale)); } } return bundleMessage.orElse(messageTemplate); } private Optional loadMessageFromBundle(String messageTemplate, ResourceBundle resourceBundle) { if (resourceBundle == null) { return Optional.empty(); } try { //noinspection ConstantConditions return Optional.ofNullable(resourceBundle.getString(messageTemplate)); } catch (MissingResourceException ignored) { return Optional.empty(); } } @SuppressWarnings("ConstantConditions") private MessageInterpolatorContext buildHibernateContext(Map messageParams, ValidationEnvironment validationEnvironment) { Object validatedValue = validationEnvironment.getValidatedValue(); ConstraintAnnotationDescriptor annotationDescriptor = new ConstraintAnnotationDescriptor.Builder<>(BridgeAnnotation.class).build(); ConstraintDescriptorImpl constraintDescriptor = new ConstraintDescriptorImpl<>( ConstraintHelper.forAllBuiltinConstraints(), null, annotationDescriptor, ConstraintLocationKind.FIELD, ConstraintType.GENERIC ); Map expressionVariables = StandardELVariables.standardELVars(validationEnvironment); Class rootBeanType = null; Path propertyPath = null; return new MessageInterpolatorContext( constraintDescriptor, validatedValue, rootBeanType, propertyPath, messageParams, expressionVariables, ExpressionLanguageFeatureLevel.BEAN_PROPERTIES, true); } private org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator hibernateInterpolator() { return new org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator(); } /// we just need an annotation to compile - we never use its @SuppressWarnings("unused") @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Constraint(validatedBy = {}) private @interface BridgeAnnotation { String message() default ""; Class[] groups() default {}; Class[] payload() default {}; } private static class ValidationErrorType implements ErrorClassification { private final ResultPath fieldOrArgumentPath; private final GraphQLDirective directive; ValidationErrorType(ResultPath fieldOrArgumentPath, GraphQLDirective directive) { this.fieldOrArgumentPath = fieldOrArgumentPath; this.directive = directive; } @Override public Object toSpecification(GraphQLError error) { Map map = new LinkedHashMap<>(); map.put("type", "ExtendedValidationError"); map.put("validatedPath", fieldOrArgumentPath.toList()); if (directive != null) { map.put("constraint", "@" + directive.getName()); } return map; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy