Skip to content
Closed
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
32 changes: 28 additions & 4 deletions src/main/java/graphql/ExecutionInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.dataloader.DataLoaderRegistry;

import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
Expand All @@ -22,21 +23,23 @@ public class ExecutionInput {
private final Map<String, Object> variables;
private final DataLoaderRegistry dataLoaderRegistry;
private final CacheControl cacheControl;
private final Locale locale;


public ExecutionInput(String query, String operationName, Object context, Object root, Map<String, Object> variables) {
this(query, operationName, context, root, variables, new DataLoaderRegistry(), null);
this(query, operationName, context, root, variables, new DataLoaderRegistry(), null, Locale.getDefault());
}

@Internal
private ExecutionInput(String query, String operationName, Object context, Object root, Map<String, Object> variables, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl) {
private ExecutionInput(String query, String operationName, Object context, Object root, Map<String, Object> variables, DataLoaderRegistry dataLoaderRegistry, CacheControl cacheControl, Locale locale) {
this.query = query;
this.operationName = operationName;
this.context = context;
this.root = root;
this.variables = variables;
this.dataLoaderRegistry = dataLoaderRegistry;
this.cacheControl = cacheControl;
this.locale = locale;
}

/**
Expand Down Expand Up @@ -88,6 +91,13 @@ public CacheControl getCacheControl() {
return cacheControl;
}

/**
* @return the {@link java.util.Locale} that the query is executing within
*/
public Locale getLocale() {
return locale;
}

/**
* This helps you transform the current ExecutionInput object into another one by starting a builder with all
* the current values and allows you to transform it how you want.
Expand All @@ -104,7 +114,8 @@ public ExecutionInput transform(Consumer<Builder> builderConsumer) {
.root(this.root)
.dataLoaderRegistry(this.dataLoaderRegistry)
.cacheControl(this.cacheControl)
.variables(this.variables);
.variables(this.variables)
.locale(this.locale);

builderConsumer.accept(builder);

Expand Down Expand Up @@ -151,6 +162,7 @@ public static class Builder {
private Map<String, Object> variables = Collections.emptyMap();
private DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
private CacheControl cacheControl;
private Locale locale = Locale.getDefault();

public Builder query(String query) {
this.query = query;
Expand Down Expand Up @@ -213,8 +225,20 @@ public Builder cacheControl(CacheControl cacheControl) {
return this;
}

/**
* This sets the locale that the query is executing in.
*
* @param locale the locale to use
*
* @return this builder
*/
public Builder locale(Locale locale) {
this.locale = locale;
return this;
}

public ExecutionInput build() {
return new ExecutionInput(query, operationName, context, root, variables, dataLoaderRegistry, cacheControl);
return new ExecutionInput(query, operationName, context, root, variables, dataLoaderRegistry, cacheControl, locale);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/graphql/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ private List<ValidationError> validate(ExecutionInput executionInput, Document d
InstrumentationContext<List<ValidationError>> validationCtx = instrumentation.beginValidation(new InstrumentationValidationParameters(executionInput, document, graphQLSchema, instrumentationState));

Validator validator = new Validator();
List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document);
List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document, executionInput.getLocale());

validationCtx.onCompleted(validationErrors, null);
return validationErrors;
Expand Down
72 changes: 72 additions & 0 deletions src/main/java/graphql/i18n/I18N.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package graphql.i18n;

import graphql.Internal;
import graphql.VisibleForTesting;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import static graphql.Assert.assertNotNull;
import static graphql.Assert.assertShouldNeverHappen;

@Internal
public class I18N {

/**
* This enum is a type safe way to control what resource bundle to load from
*/
public enum BundleType {
Validation,
Execution,
General;

private final String baseName;

BundleType() {
this.baseName = "i18n." + this.name();
}
}

private final ResourceBundle resourceBundle;

@VisibleForTesting
protected I18N(BundleType bundleType, Locale locale) {
assertNotNull(bundleType);
assertNotNull(locale);
this.resourceBundle = ResourceBundle.getBundle(bundleType.baseName, locale);
}

public ResourceBundle getResourceBundle() {
return resourceBundle;
}

public static I18N i18n(BundleType bundleType, Locale locale) {
return new I18N(bundleType, locale);
}


/**
* Creates an I18N message using the key and arguments
*
* @param msgKey the key in the underlying message bundle
* @param msgArgs the message arguments
*
* @return the formatted I18N message
*/
@SuppressWarnings("UnnecessaryLocalVariable")
public String msg(String msgKey, Object... msgArgs) {
String msgPattern = null;
try {
msgPattern = resourceBundle.getString(msgKey);
} catch (MissingResourceException e) {
assertShouldNeverHappen("There must be a resource bundle key called %s", msgKey);
}

String formattedMsg = new MessageFormat(msgPattern).format(msgArgs);
return formattedMsg;
}


}
42 changes: 42 additions & 0 deletions src/main/java/graphql/i18n/I18nMsg.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package graphql.i18n;

import java.util.ArrayList;
import java.util.List;

import static java.util.Arrays.asList;

/**
* A class that represents the intention to create a I18n message
*/
public class I18nMsg {
private final String msgKey;
private final List<Object> msgArguments;

public I18nMsg(String msgKey, List<Object> msgArguments) {
this.msgKey = msgKey;
this.msgArguments = msgArguments;
}

public I18nMsg(String msgKey, Object... msgArguments) {
this.msgKey = msgKey;
this.msgArguments = asList(msgArguments);
}

public String getMsgKey() {
return msgKey;
}

public Object[] getMsgArguments() {
return msgArguments.toArray();
}

public I18nMsg argumentAt(int index, Object argument) {
List<Object> newArgs = new ArrayList<>(this.msgArguments);
newArgs.add(index, argument);
return new I18nMsg(this.msgKey, newArgs);
}

public String toI18n(I18N i18N) {
return i18N.msg(msgKey, msgArguments);
}
}
2 changes: 1 addition & 1 deletion src/main/java/graphql/nextgen/GraphQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ private List<ValidationError> validate(ExecutionInput executionInput, Document d
InstrumentationContext<List<ValidationError>> validationCtx = instrumentation.beginValidation(new InstrumentationValidationParameters(executionInput, document, graphQLSchema, instrumentationState));

Validator validator = new Validator();
List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document);
List<ValidationError> validationErrors = validator.validateDocument(graphQLSchema, document, executionInput.getLocale());

validationCtx.onCompleted(validationErrors, null);
return validationErrors;
Expand Down
66 changes: 54 additions & 12 deletions src/main/java/graphql/validation/AbstractRule.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package graphql.validation;


import java.util.ArrayList;
import java.util.List;

import graphql.Internal;
import graphql.i18n.I18nMsg;
import graphql.language.Argument;
import graphql.language.Directive;
import graphql.language.Document;
Expand All @@ -20,20 +18,64 @@
import graphql.language.VariableDefinition;
import graphql.language.VariableReference;

import java.util.ArrayList;
import java.util.List;

import static java.lang.System.arraycopy;
import static java.util.Collections.singletonList;

@Internal
public class AbstractRule {

private final ValidationContext validationContext;
private final ValidationErrorCollector validationErrorCollector;

private final ValidationUtil validationUtil;

private boolean visitFragmentSpreads;

private ValidationUtil validationUtil = new ValidationUtil();

public AbstractRule(ValidationContext validationContext, ValidationErrorCollector validationErrorCollector) {
this.validationContext = validationContext;
this.validationErrorCollector = validationErrorCollector;
this.validationUtil = new ValidationUtil();
}

/**
* Creates an I18N message using the {@link graphql.i18n.I18nMsg}
*
* @param validationErrorType the type of validation failure
* @param i18nMsg the i18n message object
*
* @return the formatted I18N message
*/
public String i18n(ValidationErrorType validationErrorType, I18nMsg i18nMsg) {
return i18n(validationErrorType, i18nMsg.getMsgKey(), i18nMsg.getMsgArguments());
}

/**
* Creates an I18N message using the key and arguments
*
* @param validationErrorType the type of validation failure
* @param msgKey the key in the underlying message bundle
* @param msgArgs the message arguments
*
* @return the formatted I18N message
*/
public String i18n(ValidationErrorType validationErrorType, String msgKey, Object... msgArgs) {
Object[] params = new Object[msgArgs.length + 1];
params[0] = mkTypeAndPath(validationErrorType);
arraycopy(msgArgs, 0, params, 1, msgArgs.length);

return validationContext.i18n(msgKey, params);
}

private String mkTypeAndPath(ValidationErrorType validationErrorType) {
List<String> queryPath = getQueryPath();
StringBuilder sb = new StringBuilder();
sb.append(validationErrorType);
if (queryPath != null) {
sb.append("@[").append(String.join("/", queryPath)).append("]");
}
return sb.toString();
}

public boolean isVisitFragmentSpreads() {
Expand All @@ -49,20 +91,20 @@ public ValidationUtil getValidationUtil() {
return validationUtil;
}

public void setValidationUtil(ValidationUtil validationUtil) {
this.validationUtil = validationUtil;
}

public void addError(ValidationErrorType validationErrorType, List<? extends Node> locations, String description) {
List<SourceLocation> locationList = new ArrayList<>();
for (Node node : locations) {
locationList.add(node.getSourceLocation());
}
validationErrorCollector.addError(new ValidationError(validationErrorType, locationList, description, getQueryPath()));
validationErrorCollector.addError(
new ValidationError(validationErrorType, locationList, description, getQueryPath())
);
}

public void addError(ValidationErrorType validationErrorType, SourceLocation location, String description) {
validationErrorCollector.addError(new ValidationError(validationErrorType, location, description, getQueryPath()));
validationErrorCollector.addError(
new ValidationError(validationErrorType, singletonList(location), description, getQueryPath())
);
}

public List<ValidationError> getErrors() {
Expand Down
Loading