convert(@Tainted @Nullable I value) {
- try {
- return Optional.ofNullable(converter.apply(value));
- } catch (FormFieldException e) {
- throw e.propagate(name);
- }
- }
-
- /**
- * Convert and validate a raw user-supplied value from a map.
- *
- * This is the same as saying: {@code field.convert(valueMap.get(field.name())}
- *
- * @throws FormFieldException if value does not meet expected contracts.
- */
- @Detainted
- public Optional extract(@Tainted Map valueMap) {
- return convert(valueMap.get(name));
- }
-
- /**
- * Convert and validate a raw user-supplied value from an untyped JSON map.
- *
- * @throws FormFieldException if value is wrong type or does not meet expected contracts.
- */
- @Detainted
- public Optional extractUntyped(@Tainted Map jsonMap) {
- Object value = jsonMap.get(name);
- I castedValue;
- try {
- castedValue = typeIn.cast(value);
- } catch (ClassCastException e) {
- throw new FormFieldException(String.format("Type error: got: %s, expected: %s",
- value.getClass().getSimpleName(),
- typeIn.getSimpleName())).propagate(name);
- }
- return convert(castedValue);
- }
-
- /**
- * Returns a builder of this object, which can be used to further restrict validation.
- *
- * @see #asBuilderNamed(String)
- */
- public Builder asBuilder() {
- return new Builder<>(name, typeIn, typeOut, converter);
- }
-
- /** Same as {@link #asBuilder()} but changes the field name. */
- public Builder asBuilderNamed(String newName) {
- checkArgument(!newName.isEmpty());
- return new Builder<>(newName, typeIn, typeOut, converter);
- }
-
- /**
- * Mutable builder for {@link FormField}.
- *
- * @param input value type
- * @param output value type
- */
- public static final class Builder {
- private final String name;
- private final Class typeIn;
- private final Class typeOut;
- private Function converter;
-
- private Builder(String name, Class typeIn, Class typeOut, Function converter) {
- this.name = name;
- this.typeIn = typeIn;
- this.typeOut = typeOut;
- this.converter = converter;
- }
-
- /** Causes {@code defaultValue} to be substituted if value is {@code null}. */
- public Builder withDefault(O defaultValue) {
- return transform(new DefaultFunction<>(checkNotNull(defaultValue)));
- }
-
- /** Ensure value is not {@code null}. */
- public Builder required() {
- return transform(Builder::checkNotNullTransform);
- }
-
- /**
- * Transform empty values into {@code null}.
- *
- * @throws IllegalStateException if current output type is not a {@link CharSequence} or
- * {@link Collection}.
- */
- public Builder emptyToNull() {
- checkState(CharSequence.class.isAssignableFrom(typeOut)
- || Collection.class.isAssignableFrom(typeOut));
- return transform(
- input ->
- ((input instanceof CharSequence) && (((CharSequence) input).length() == 0))
- || ((input instanceof Collection) && ((Collection>) input).isEmpty())
- ? null
- : input);
- }
-
- /**
- * Modify {@link String} input to remove whitespace around the sides.
- *
- * {@code null} values are passed through.
- *
- * @throws IllegalStateException if current output type is not a String.
- */
- public Builder trimmed() {
- checkState(String.class.isAssignableFrom(typeOut));
- @SuppressWarnings("unchecked")
- Function trimFunction =
- (Function)
- ((Function) input -> input != null ? input.trim() : null);
- return transform(String.class, trimFunction);
- }
-
- /**
- * Modify {@link String} input to be uppercase.
- *
- * {@code null} values are passed through.
- *
- * @throws IllegalStateException if current output type is not a String.
- */
- public Builder uppercased() {
- checkState(String.class.isAssignableFrom(typeOut));
- @SuppressWarnings("unchecked")
- Function funk =
- (Function)
- ((Function)
- input -> input != null ? input.toUpperCase(Locale.ENGLISH) : null);
- return transform(String.class, funk);
- }
-
- /**
- * Modify {@link String} input to be lowercase.
- *
- * {@code null} values are passed through.
- *
- * @throws IllegalStateException if current output type is not a String.
- */
- public Builder lowercased() {
- checkState(String.class.isAssignableFrom(typeOut));
- @SuppressWarnings("unchecked")
- Function funk =
- (Function)
- ((Function)
- input -> input != null ? input.toLowerCase(Locale.ENGLISH) : null);
- return transform(String.class, funk);
- }
-
- /**
- * Ensure input matches {@code pattern}.
- *
- * {@code null} values are passed through.
- *
- * @param pattern is used to validate the user input. It matches against the whole string, so
- * you don't need to use the ^$ characters.
- * @param errorMessage is a helpful error message, which should include an example. If this is
- * not provided, a default error message will be shown that includes the regexp pattern.
- * @throws IllegalStateException if current output type is not a {@link CharSequence}.
- * @see #matches(Pattern)
- */
- public Builder matches(Pattern pattern, @Nullable String errorMessage) {
- checkState(CharSequence.class.isAssignableFrom(typeOut));
- return transform(
- new MatchesFunction<>(checkNotNull(pattern), Optional.ofNullable(errorMessage)));
- }
-
- /** Alias for {@link #matches(Pattern, String) matches(pattern, null)} */
- public Builder matches(Pattern pattern) {
- return matches(pattern, null);
- }
-
- /**
- * Removes all characters not in {@code matcher}.
- *
- *
{@code null} values are passed through.
- *
- * @param matcher indicates which characters are to be retained
- * @throws IllegalStateException if current output type is not a {@link CharSequence}
- */
- public Builder retains(CharMatcher matcher) {
- checkState(CharSequence.class.isAssignableFrom(typeOut));
- @SuppressWarnings("unchecked") // safe due to checkState call
- Function function =
- (Function) new RetainFunction(checkNotNull(matcher));
- return transform(String.class, function);
- }
-
- /**
- * Enforce value length/size/value is within {@code range}.
- *
- * The following input value types are supported:
- *
- *
- * {@link CharSequence}: Length must be within {@code range}.
- * {@link Collection}: Size must be within {@code range}.
- * {@link Number}: Value must be within {@code range}.
- *
- *
- * {@code null} values are passed through. Please note that setting a lower bound on your
- * range does not imply {@link #required()}, as range checking only applies to non-{@code null}
- * values.
- *
- * @throws IllegalStateException if current output type is not one of the above types.
- */
- public Builder range(Range range) {
- checkState(CharSequence.class.isAssignableFrom(typeOut)
- || Collection.class.isAssignableFrom(typeOut)
- || Number.class.isAssignableFrom(typeOut));
- return transform(new RangeFunction<>(checkNotNull(range)));
- }
-
- /**
- * Enforce value be a member of {@code values}.
- *
- * {@code null} values are passed through.
- *
- * @throws IllegalArgumentException if {@code values} is empty.
- */
- public Builder in(Set values) {
- checkArgument(!values.isEmpty());
- return transform(new InFunction<>(values));
- }
-
- /**
- * Performs arbitrary type transformation from {@code O} to {@code T}.
- *
- * Your {@code transform} function is expected to pass-through {@code null} values as a
- * no-op, since it's up to {@link #required()} to block them. You might also want to consider
- * using a try block that rethrows exceptions as {@link FormFieldException}.
- *
- *
Here's an example of how you'd convert from String to Integer:
- *
- *
- * FormField.named("foo", String.class)
- * .transform(Integer.class, new Function<String, Integer>() {
- * @Nullable
- * @Override
- * public Integer apply(@Nullable String input) {
- * try {
- * return input != null ? Integer.parseInt(input) : null;
- * } catch (IllegalArgumentException e) {
- * throw new FormFieldException("Invalid number.", e);
- * }
- * }})
- * .build();
- *
- * @see #transform(Function)
- */
- public Builder transform(Class newType, Function transform) {
- return new Builder<>(
- name, typeIn, checkNotNull(newType), this.converter.andThen(checkNotNull(transform)));
- }
-
- /**
- * Manipulates values without changing type.
- *
- * Please see {@link #transform(Class, Function)} for information about the contract to
- * which {@code transform} is expected to conform.
- */
- public Builder transform(Function transform) {
- this.converter = this.converter.andThen(checkNotNull(transform));
- return this;
- }
-
- /**
- * Uppercases value and converts to an enum field of {@code enumClass}.
- *
- * {@code null} values are passed through.
- *
- * @throws IllegalArgumentException if {@code enumClass} is not an enum class.
- * @throws IllegalStateException if current output type is not a String.
- */
- public > Builder asEnum(Class enumClass) {
- checkArgument(enumClass.isEnum());
- checkState(String.class.isAssignableFrom(typeOut));
- return transform(enumClass, new ToEnumFunction<>(enumClass));
- }
-
- /**
- * Turns this form field into something that processes lists.
- *
- * The current object definition will be applied to each item in the list. If a
- * {@link FormFieldException} is thrown when processing an item, then its
- * {@link FormFieldException#getFieldName() fieldName} will be rewritten to include the index,
- * e.g. {@code name} becomes {@code name[0]}.
- *
- *
The outputted list will be an {@link ImmutableList}. This is not reflected in the generic
- * typing for the sake of brevity.
- *
- *
A {@code null} value for list will be passed through. List items that convert to
- * {@code null} will be discarded (since {@code ImmutableList} does not permit {@code null}
- * values).
- */
- public Builder, List> asList() {
- @SuppressWarnings("unchecked") Class> in = (Class>) (Class) List.class;
- @SuppressWarnings("unchecked") Class> out = (Class>) (Class) List.class;
- return new Builder<>(name, in, out, new ToListFunction<>(build()));
- }
-
- /**
- * Turns this form field into a split string list that applies itself to each item.
- *
- * The behavior of this method is counter-intuitive. It behaves similar to {@link #asList()}
- * in the sense that all transforms specified before this method will be applied to the
- * individual resulting list items.
- *
- *
For example, to turn a comma-delimited string into an enum list:
{@code
- *
- * private static final FormField> STATES_FIELD =
- * FormField.named("states")
- * .uppercased()
- * .asEnum(State.class)
- * .asList(Splitter.on(',').omitEmptyStrings().trimResults())
- * .build();}
- *
- * You'll notice that the transforms specified before this method are applied to each list
- * item. However unlike {@link #asList()}, if an error is thrown on an individual item, then
- * {@link FormFieldException#getFieldName()} will not contain the index.
- *
- * @throws IllegalStateException If either the current input type isn't String.
- */
- public Builder> asList(Splitter splitter) {
- checkNotNull(splitter);
- checkState(String.class.isAssignableFrom(typeIn));
- @SuppressWarnings("unchecked") Class> out = (Class>) (Class) List.class;
- @SuppressWarnings("unchecked") FormField inField = (FormField) build();
- return new Builder<>(name, String.class, out, new SplitToListFunction<>(inField, splitter));
- }
-
- /**
- * Same as {@link #asList()} but outputs an {@link ImmutableSet} instead.
- *
- * @throws IllegalStateException if you called asList() before calling this method.
- */
- public Builder, Set> asSet() {
- checkState(!List.class.isAssignableFrom(typeOut));
- @SuppressWarnings("unchecked")
- Class> setOut = (Class>) (Class) Set.class;
- @SuppressWarnings("unchecked")
- Function, Set> toSetFunction =
- (Function, Set>)
- (Function)
- ((Function, Set>)
- input -> input != null ? ImmutableSet.copyOf(input) : null);
- return asList().transform(setOut, toSetFunction);
- }
-
- /**
- * Same as {@link #asList(Splitter)} but outputs an {@link ImmutableSet} instead.
- *
- * @throws IllegalStateException If the current input type isn't String.
- */
- public Builder> asSet(Splitter splitter) {
- checkNotNull(splitter);
- checkState(String.class.isAssignableFrom(typeIn));
- @SuppressWarnings("unchecked") Class> out = (Class>) (Class) Set.class;
- @SuppressWarnings("unchecked") FormField inField = (FormField) build();
- return new Builder<>(name, String.class, out, new SplitToSetFunction<>(inField, splitter));
- }
-
- /** Creates a new {@link FormField} instance. */
- public FormField build() {
- return new FormField<>(name, typeIn, typeOut, converter);
- }
-
- private static O checkNotNullTransform(@Nullable O input) {
- if (input == null) {
- throw new FormFieldException("This field is required.");
- }
- return input;
- }
-
- private static final class DefaultFunction implements Function {
- private final O defaultValue;
-
- DefaultFunction(O defaultValue) {
- this.defaultValue = defaultValue;
- }
-
- @Nullable
- @Override
- public O apply(@Nullable O input) {
- return input != null ? input : defaultValue;
- }
- }
-
- private static final class RangeFunction implements Function {
- private final Range range;
-
- RangeFunction(Range range) {
- this.range = range;
- }
-
- @Nullable
- @Override
- public O apply(@Nullable O input) {
- if (input == null) {
- return null;
- }
- if (input instanceof CharSequence) {
- checkRangeContains(range, ((CharSequence) input).length(), "Number of characters");
- } else if (input instanceof Collection) {
- checkRangeContains(range, ((Collection>) input).size(), "Number of items");
- } else if (input instanceof Number) {
- checkRangeContains(range, ((Number) input).intValue(), "Value");
- } else {
- throw new AssertionError();
- }
- return input;
- }
-
- private void checkRangeContains(Range range, int value, String message) {
- if (!range.contains(value)) {
- throw new FormFieldException(
- String.format("%s (%,d) not in range %s", message, value, range));
- }
- }
- }
-
- private static final class InFunction implements Function {
- private final Set values;
-
- InFunction(Set values) {
- this.values = values;
- }
-
- @Nullable
- @Override
- public O apply(@Nullable O input) {
- if (input == null) {
- return null;
- }
- if (!values.contains(input)) {
- throw new FormFieldException("Unrecognized value.");
- }
- return input;
- }
- }
-
- private static final class MatchesFunction implements Function {
- private final Pattern pattern;
- private final Optional errorMessage;
-
- MatchesFunction(Pattern pattern, Optional errorMessage) {
- this.pattern = pattern;
- this.errorMessage = errorMessage;
- }
-
- @Nullable
- @Override
- public O apply(@Nullable O input) {
- if (input == null) {
- return null;
- }
- if (!pattern.matcher((CharSequence) input).matches()) {
- throw new FormFieldException(errorMessage.orElse("Must match pattern: " + pattern));
- }
- return input;
- }
- }
-
- private static final class RetainFunction implements Function {
- private final CharMatcher matcher;
-
- RetainFunction(CharMatcher matcher) {
- this.matcher = matcher;
- }
-
- @Nullable
- @Override
- public String apply(@Nullable CharSequence input) {
- if (input == null) {
- return null;
- }
- return matcher.retainFrom(input);
- }
- }
-
- private static final class ToEnumFunction> implements Function {
- private final Class enumClass;
-
- ToEnumFunction(Class enumClass) {
- this.enumClass = enumClass;
- }
-
- @Nullable
- @Override
- public C apply(@Nullable O input) {
- try {
- return input != null ? Enum.valueOf(enumClass, Ascii.toUpperCase((String) input)) : null;
- } catch (IllegalArgumentException e) {
- throw new FormFieldException(
- String.format("Enum %s does not contain '%s'", enumClass.getSimpleName(), input));
- }
- }
- }
-
- private static final class ToListFunction implements Function, List> {
- private final FormField itemField;
-
- ToListFunction(FormField itemField) {
- this.itemField = itemField;
- }
-
- @Nullable
- @Override
- public List apply(@Nullable List input) {
- if (input == null) {
- return null;
- }
- ImmutableList.Builder builder = new ImmutableList.Builder<>();
- for (int i = 0; i < input.size(); i++) {
- I inputItem = itemField.typeIn.cast(input.get(i));
- O outputItem;
- try {
- outputItem = itemField.converter.apply(inputItem);
- } catch (FormFieldException e) {
- throw e.propagate(i);
- }
- if (outputItem != null) {
- builder.add(outputItem);
- }
- }
- return builder.build();
- }
- }
-
- private static final class SplitToListFunction implements Function> {
- private final FormField itemField;
- private final Splitter splitter;
-
- SplitToListFunction(FormField itemField, Splitter splitter) {
- this.itemField = itemField;
- this.splitter = splitter;
- }
-
- @Nullable
- @Override
- public List apply(@Nullable String input) {
- return input == null
- ? null
- : Streams.stream(splitter.split(input))
- .map(itemField.converter)
- .collect(toImmutableList());
- }
- }
-
- private static final class SplitToSetFunction implements Function> {
- private final FormField itemField;
- private final Splitter splitter;
-
- SplitToSetFunction(FormField itemField, Splitter splitter) {
- this.itemField = itemField;
- this.splitter = splitter;
- }
-
- @Nullable
- @Override
- public Set apply(@Nullable String input) {
- return input == null
- ? null
- : Streams.stream(splitter.split(input))
- .map(itemField.converter)
- .collect(toImmutableSet());
- }
- }
- }
-}
diff --git a/core/src/main/java/google/registry/ui/forms/FormFieldException.java b/core/src/main/java/google/registry/ui/forms/FormFieldException.java
deleted file mode 100644
index d59959405..000000000
--- a/core/src/main/java/google/registry/ui/forms/FormFieldException.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.forms;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import javax.annotation.CheckReturnValue;
-import javax.annotation.Detainted;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.NotThreadSafe;
-
-/**
- * Exception thrown when a form field contains a bad value.
- *
- * You can safely throw {@code FormFieldException} from within your validator functions, and the
- * field name will automatically be propagated into the exception object for you.
- *
- *
The way that field names work is a bit complicated, because we need to support complex nested
- * field names like {@code foo[3].bar}. So what happens is the original exception will be thrown by
- * a {@link FormField} validator without the field set. Then as the exception bubbles up the stack,
- * it'll be caught by the {@link FormField#convert(Object) convert} method, which then prepends the
- * name of that component. Then when the exception reaches the user, the {@link #getFieldName()}
- * method will produce the fully-qualified field name.
- *
- *
This propagation mechanism is also very important when writing {@link
- * FormField.Builder#transform} functions, which oftentimes will not know the name of the field
- * they're validating.
- */
-@NotThreadSafe
-@SuppressWarnings("OverrideThrowableToString")
-public final class FormFieldException extends FormException {
-
- private final List names = new ArrayList<>();
-
- @Nullable
- private String lazyFieldName;
-
- /**
- * Creates a new {@link FormFieldException}
- *
- * This exception should only be thrown from within a {@link FormField} converter function.
- * The field name will automatically be propagated into the exception object for you.
- *
- * @param userMessage should be a friendly message that's safe to show to the user.
- */
- public FormFieldException(@Detainted String userMessage) {
- super(checkNotNull(userMessage, "userMessage"), null);
- }
-
- /**
- * Creates a new {@link FormFieldException}
- *
- *
This exception should only be thrown from within a {@link FormField} converter function.
- * The field name will automatically be propagated into the exception object for you.
- *
- * @param userMessage should be a friendly message that's safe to show to the user.
- * @param cause the original cause of this exception (non-null).
- */
- public FormFieldException(@Detainted String userMessage, Throwable cause) {
- super(checkNotNull(userMessage, "userMessage"), checkNotNull(cause, "cause"));
- }
-
- /**
- * Creates a new {@link FormFieldException} for a particular form field.
- *
- *
This exception should only be thrown from within a {@link FormField} MAP converter function
- * in situations where you're performing additional manual validation.
- *
- * @param userMessage should be a friendly message that's safe to show to the user.
- */
- public FormFieldException(FormField, ?> field, @Detainted String userMessage) {
- this(field.name(), userMessage);
- }
-
- /**
- * Creates a new {@link FormFieldException} for a particular field name.
- *
- * @param field name corresponding to a {@link FormField#name()}
- * @param userMessage friendly message that's safe to show to the user
- */
- public FormFieldException(String field, @Detainted String userMessage) {
- super(checkNotNull(userMessage, "userMessage"), null);
- propagateImpl(field);
- }
-
- /** Returns the fully-qualified name (JavaScript syntax) of the form field causing this error. */
- public String getFieldName() {
- String fieldName = lazyFieldName;
- if (fieldName == null) {
- lazyFieldName = fieldName = getFieldNameImpl();
- }
- return fieldName;
- }
-
- private String getFieldNameImpl() {
- checkState(!names.isEmpty(),
- "FormFieldException was thrown outside FormField infrastructure!");
- Iterator namesIterator = Lists.reverse(names).iterator();
- StringBuilder result = new StringBuilder((String) namesIterator.next());
- while (namesIterator.hasNext()) {
- Object name = namesIterator.next();
- if (name instanceof String) {
- result.append('.').append(name);
- } else if (name instanceof Integer) {
- result.append('[').append(name).append(']');
- } else {
- throw new AssertionError();
- }
- }
- return result.toString();
- }
-
- /**
- * Returns self with {@code name} prepended, for propagating exceptions up the stack.
- *
- * This would be package-private except that it needs to be called by a test class in another
- * package.
- */
- @CheckReturnValue
- public FormFieldException propagate(String name) {
- return propagateImpl(name);
- }
-
- /** Returns self with {@code index} prepended, for propagating exceptions up the stack. */
- @CheckReturnValue
- FormFieldException propagate(int index) {
- return propagateImpl(index);
- }
-
- /** Returns self with {@code name} prepended, for propagating exceptions up the stack. */
- private FormFieldException propagateImpl(Object name) {
- lazyFieldName = null;
- names.add(checkNotNull(name));
- return this;
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- return this == obj
- || (obj instanceof FormFieldException
- && Objects.equals(getCause(), ((FormFieldException) obj).getCause())
- && Objects.equals(getMessage(), ((FormFieldException) obj).getMessage())
- && Objects.equals(names, ((FormFieldException) obj).names));
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getCause(), getMessage(), getFieldName());
- }
-
- @Override
- public String toString() {
- return toStringHelper(getClass())
- .add("fieldName", getFieldName())
- .add("message", getMessage())
- .add("cause", getCause())
- .toString();
- }
-}
diff --git a/core/src/main/java/google/registry/ui/forms/FormFields.java b/core/src/main/java/google/registry/ui/forms/FormFields.java
deleted file mode 100644
index 612c03093..000000000
--- a/core/src/main/java/google/registry/ui/forms/FormFields.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.forms;
-
-import static com.google.common.collect.Range.atMost;
-import static com.google.common.collect.Range.closed;
-import static com.google.common.collect.Range.singleton;
-import static java.util.Locale.getISOCountries;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.re2j.Pattern;
-
-/** Utility class of {@link FormField} objects for validating EPP related things. */
-public final class FormFields {
-
- private static final Pattern WHITESPACE = Pattern.compile("[ \\t\\r\\n]+");
- /**
- * Form field that applies XML Schema Token cleanup to input.
- *
- *
This trims the input and collapses whitespace.
- *
- * @see XSD Datatypes - token
- */
- public static final FormField XS_TOKEN =
- FormField.named("xsToken")
- .emptyToNull()
- .trimmed()
- .transform(input -> input != null ? WHITESPACE.matcher(input).replaceAll(" ") : null)
- .build();
-
- /**
- * Form field that ensures input does not contain tabs, line feeds, or carriage returns.
- *
- * @see
- * XSD Datatypes - normalizedString
- */
- public static final FormField XS_NORMALIZED_STRING =
- FormField.named("xsNormalizedString")
- .emptyToNull()
- .matches(Pattern.compile("[^\\t\\r\\n]*"), "Must not contain tabs or multiple lines.")
- .build();
-
- /**
- * Form field for +E164 phone numbers with a dot after the country prefix.
- *
- * @see RFC 5733 - EPP - Formal Syntax
- */
- public static final FormField PHONE_NUMBER =
- XS_TOKEN.asBuilderNamed("phoneNumber")
- .range(atMost(17))
- .matches(Pattern.compile("(\\+[0-9]{1,3}\\.[0-9]{1,14})?"),
- "Must be a valid +E.164 phone number, e.g. +1.2125650000")
- .build();
-
- /** Form field for EPP client identifiers. */
- public static final FormField CLID = XS_TOKEN.asBuilderNamed("clid")
- .range(closed(3, 16))
- .build();
-
- /** Form field for passwords (see pwType in epp.xsd). */
- public static final FormField PASSWORD = XS_TOKEN.asBuilderNamed("password")
- .range(closed(6, 16))
- .build();
-
- /** Form field for non-empty tokens (see minToken in eppcom.xsd). */
- public static final FormField MIN_TOKEN = XS_TOKEN.asBuilderNamed("minToken")
- .emptyToNull()
- .build();
-
- /** Form field for nameType (see rde-registrar/notification). */
- public static final FormField NAME = XS_NORMALIZED_STRING.asBuilderNamed("name")
- .range(closed(1, 255))
- .build();
-
- /** Form field for {@code labelType} from {@code eppcom.xsd}. */
- public static final FormField LABEL = XS_TOKEN.asBuilderNamed("label")
- .range(closed(1, 255))
- .build();
-
- /** Email address form field. */
- public static final FormField EMAIL = XS_TOKEN.asBuilderNamed("email")
- .matches(Pattern.compile("[^@]+@[^@.]+\\.[^@]+"), "Please enter a valid email address.")
- .build();
-
- /** Two-letter ISO country code form field. */
- public static final FormField COUNTRY_CODE =
- XS_TOKEN.asBuilderNamed("countryCode")
- .range(singleton(2))
- .uppercased()
- .in(ImmutableSet.copyOf(getISOCountries()))
- .build();
-
- /**
- * Ensure value is an EPP Repository Object IDentifier (ROID).
- *
- * @see Shared Structure Schema
- */
- public static final FormField ROID = XS_TOKEN.asBuilderNamed("roid")
- .matches(Pattern.compile("(\\w|_){1,80}-\\w{1,8}"),
- "Please enter a valid EPP ROID, e.g. SH8013-REP")
- .build();
-
- private FormFields() {}
-}
diff --git a/core/src/main/java/google/registry/ui/forms/package-info.java b/core/src/main/java/google/registry/ui/forms/package-info.java
deleted file mode 100644
index b01aa93dd..000000000
--- a/core/src/main/java/google/registry/ui/forms/package-info.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/** Web application backend form processing utilities. */
-@javax.annotation.ParametersAreNonnullByDefault
-package google.registry.ui.forms;
diff --git a/core/src/main/java/google/registry/ui/html/error.html b/core/src/main/java/google/registry/ui/html/error.html
deleted file mode 100644
index 44568bb11..000000000
--- a/core/src/main/java/google/registry/ui/html/error.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-Server Error
-
-
-500. That's an error.
-
Sorry, but the server encountered an error while processing your request.
diff --git a/core/src/main/java/google/registry/ui/html/index.html b/core/src/main/java/google/registry/ui/html/index.html
deleted file mode 100644
index 9903e3e51..000000000
--- a/core/src/main/java/google/registry/ui/html/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
Nomulus
-
-If this page doesn't change automatically, please go
-to https://www.registry.google/console
diff --git a/core/src/main/java/google/registry/ui/package-info.java b/core/src/main/java/google/registry/ui/package-info.java
deleted file mode 100644
index ddff37c68..000000000
--- a/core/src/main/java/google/registry/ui/package-info.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-@javax.annotation.ParametersAreNonnullByDefault
-package google.registry.ui;
diff --git a/core/src/main/java/google/registry/ui/server/StateCode.java b/core/src/main/java/google/registry/ui/server/StateCode.java
deleted file mode 100644
index 4bf7ef12c..000000000
--- a/core/src/main/java/google/registry/ui/server/StateCode.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.server;
-
-import com.google.common.collect.ImmutableBiMap;
-
-/**
- * Bimap of state codes and names for the US Regime.
- *
- * @see State Table
- */
-public final class StateCode {
-
- public static final ImmutableBiMap US_MAP =
- new ImmutableBiMap.Builder()
- .put("AL", "Alabama")
- .put("AK", "Alaska")
- .put("AZ", "Arizona")
- .put("AR", "Arkansas")
- .put("CA", "California")
- .put("CO", "Colorado")
- .put("CT", "Connecticut")
- .put("DE", "Delaware")
- .put("FL", "Florida")
- .put("GA", "Georgia")
- .put("HI", "Hawaii")
- .put("ID", "Idaho")
- .put("IL", "Illinois")
- .put("IN", "Indiana")
- .put("IA", "Iowa")
- .put("KS", "Kansas")
- .put("KY", "Kentucky")
- .put("LA", "Louisiana")
- .put("ME", "Maine")
- .put("MD", "Maryland")
- .put("MA", "Massachusetts")
- .put("MI", "Michigan")
- .put("MN", "Minnesota")
- .put("MS", "Mississippi")
- .put("MO", "Missouri")
- .put("MT", "Montana")
- .put("NE", "Nebraska")
- .put("NV", "Nevada")
- .put("NH", "New Hampshire")
- .put("NJ", "New Jersey")
- .put("NM", "New Mexico")
- .put("NY", "New York")
- .put("NC", "North Carolina")
- .put("ND", "North Dakota")
- .put("OH", "Ohio")
- .put("OK", "Oklahoma")
- .put("OR", "Oregon")
- .put("PA", "Pennsylvania")
- .put("RI", "Rhode Island")
- .put("SC", "South Carolina")
- .put("SD", "South Dakota")
- .put("TN", "Tennessee")
- .put("TX", "Texas")
- .put("UT", "Utah")
- .put("VT", "Vermont")
- .put("VA", "Virginia")
- .put("WA", "Washington")
- .put("WV", "West Virginia")
- .put("WI", "Wisconsin")
- .put("WY", "Wyoming")
- .put("DC", "Washington DC")
- .build();
-
- private StateCode() {}
-}
diff --git a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
index 72b29db82..1774d8087 100644
--- a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
+++ b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
@@ -40,7 +40,6 @@ import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
-import google.registry.ui.forms.FormException;
import google.registry.ui.server.console.ConsoleApiAction;
import google.registry.ui.server.console.ConsoleApiParams;
import jakarta.inject.Inject;
@@ -166,7 +165,7 @@ public class ContactAction extends ConsoleApiAction {
try {
checkContactRequirements(oldContacts, newContacts);
- } catch (FormException e) {
+ } catch (ContactRequirementException e) {
logger.atWarning().withCause(e).log(
"Error processing contacts post request for registrar: %s", registrarId);
throw new IllegalArgumentException(e);
@@ -196,7 +195,7 @@ public class ContactAction extends ConsoleApiAction {
/**
* Enforces business logic checks on registrar contacts.
*
- * @throws FormException if the checks fail.
+ * @throws ContactRequirementException if the checks fail.
*/
private static void checkContactRequirements(
ImmutableSet existingContacts, ImmutableSet updatedContacts) {
@@ -299,7 +298,7 @@ public class ContactAction extends ConsoleApiAction {
}
/** Thrown when a set of contacts doesn't meet certain constraints. */
- private static class ContactRequirementException extends FormException {
+ private static class ContactRequirementException extends RuntimeException {
ContactRequirementException(String msg) {
super(msg);
}
diff --git a/core/src/main/java/google/registry/ui/server/package-info.java b/core/src/main/java/google/registry/ui/server/package-info.java
deleted file mode 100644
index d5246ed76..000000000
--- a/core/src/main/java/google/registry/ui/server/package-info.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-@javax.annotation.ParametersAreNonnullByDefault
-package google.registry.ui.server;
diff --git a/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java b/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java
index c238b847e..33ef665b5 100644
--- a/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java
+++ b/core/src/test/java/google/registry/module/frontend/FrontendTestComponent.java
@@ -30,7 +30,6 @@ import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules;
import google.registry.request.auth.AuthModule;
-import google.registry.ui.ConsoleDebug;
import google.registry.util.UtilsModule;
import jakarta.inject.Singleton;
@@ -40,7 +39,6 @@ import jakarta.inject.Singleton;
AuthModule.class,
CloudTasksUtilsModule.class,
RegistryConfig.ConfigModule.class,
- ConsoleDebug.ConsoleConfigModule.class,
CredentialModule.class,
CustomLogicFactoryModule.class,
CloudTasksUtilsModule.class,
diff --git a/core/src/test/java/google/registry/server/RegistryTestServer.java b/core/src/test/java/google/registry/server/RegistryTestServer.java
index 4439c786e..0a3ccaa6d 100644
--- a/core/src/test/java/google/registry/server/RegistryTestServer.java
+++ b/core/src/test/java/google/registry/server/RegistryTestServer.java
@@ -35,12 +35,6 @@ public final class RegistryTestServer {
public static final ImmutableMap RUNFILES =
new ImmutableMap.Builder()
- .put(
- "/index.html",
- PROJECT_ROOT.resolve("core/src/main/java/google/registry/ui/html/index.html"))
- .put(
- "/error.html",
- PROJECT_ROOT.resolve("core/src/main/java/google/registry/ui/html/error.html"))
.put("/console/*", PROJECT_ROOT.resolve("console-webapp/staged/dist"))
.build();
diff --git a/core/src/test/java/google/registry/server/RegistryTestServerMain.java b/core/src/test/java/google/registry/server/RegistryTestServerMain.java
index 437667918..9ef8ca195 100644
--- a/core/src/test/java/google/registry/server/RegistryTestServerMain.java
+++ b/core/src/test/java/google/registry/server/RegistryTestServerMain.java
@@ -28,7 +28,6 @@ import google.registry.request.auth.AuthResult;
import google.registry.request.auth.OidcTokenAuthenticationMechanism;
import google.registry.testing.DatabaseHelper;
import google.registry.tools.params.HostAndPortParameter;
-import google.registry.ui.ConsoleDebug;
import java.util.List;
/** Command-line interface for {@link RegistryTestServer}. */
@@ -42,11 +41,6 @@ public final class RegistryTestServerMain {
private static final String LIGHT_PURPLE = "\u001b[38;5;139m";
private static final String ORANGE = "\u001b[1;38;5;172m";
- @Parameter(
- names = "--mode",
- description = "UI console debug mode. RAW allows live editing; DEBUG allows rename testing.")
- private ConsoleDebug mode = ConsoleDebug.PRODUCTION;
-
@Parameter(
names = "--address",
description = "Listening address.",
@@ -67,14 +61,10 @@ public final class RegistryTestServerMain {
arity = 1)
private boolean loginIsAdmin = true;
- @Parameter(
- names = "--jetty_debug",
- description = "Enables Jetty debug logging.")
+ @Parameter(names = "--jetty_debug", description = "Enables Jetty debug logging.")
private boolean jettyDebug;
- @Parameter(
- names = "--jetty_verbose",
- description = "Enables Jetty verbose logging.")
+ @Parameter(names = "--jetty_verbose", description = "Enables Jetty verbose logging.")
private boolean jettyVerbose;
@Parameter(
@@ -96,7 +86,6 @@ public final class RegistryTestServerMain {
}
private void run() throws Throwable {
- ConsoleDebug.set(mode);
if (jettyDebug) {
System.setProperty("DEBUG", "true");
}
@@ -105,7 +94,7 @@ public final class RegistryTestServerMain {
}
System.out.printf(
- """
+"""
CHARLESTON ROAD REGISTRY SHARED REGISTRATION SYSTEM
ICANN-GTLD-AGB-20120604
diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java
deleted file mode 100644
index de46c893b..000000000
--- a/core/src/test/java/google/registry/ui/forms/FormFieldExceptionTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.forms;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import com.google.common.testing.NullPointerTester;
-import org.junit.jupiter.api.Test;
-
-/** Unit tests for {@link FormFieldException}. */
-class FormFieldExceptionTest {
-
- @Test
- void testGetFieldName_multiplePropagations_joinsUsingJsonNotation() {
- assertThat(
- new FormFieldException("This field is required.")
- .propagate("attack")
- .propagate("cat")
- .propagate(0)
- .propagate("lol")
- .getFieldName())
- .isEqualTo("lol[0].cat.attack");
- }
-
- @Test
- void testGetFieldName_singlePropagations_noFancyJoining() {
- assertThat(
- new FormFieldException("This field is required.")
- .propagate("cat")
- .getFieldName())
- .isEqualTo("cat");
- }
-
- @Test
- void testGetFieldName_noPropagations_throwsIse() {
- assertThrows(
- IllegalStateException.class,
- () -> new FormFieldException("This field is required.").getFieldName());
- }
-
- @Test
- void testNullness() {
- NullPointerTester tester = new NullPointerTester()
- .setDefault(FormField.class, FormField.named("love").build());
- tester.testAllPublicConstructors(FormFieldException.class);
- tester.testAllPublicStaticMethods(FormFieldException.class);
- tester.testAllPublicInstanceMethods(new FormFieldException("lol"));
- }
-}
diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldTest.java
deleted file mode 100644
index 700ac3475..000000000
--- a/core/src/test/java/google/registry/ui/forms/FormFieldTest.java
+++ /dev/null
@@ -1,486 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.forms;
-
-import static com.google.common.collect.Range.atLeast;
-import static com.google.common.collect.Range.atMost;
-import static com.google.common.collect.Range.closed;
-import static com.google.common.truth.Truth.assertThat;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.testing.NullPointerTester;
-import com.google.re2j.Pattern;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import org.junit.jupiter.api.Test;
-
-/** Unit tests for {@link FormField}. */
-class FormFieldTest {
-
- private enum ICanHazEnum {
- LOL,
- CAT
- }
-
- @Test
- void testConvert_nullString_notPresent() {
- assertThat(FormField.named("lol").build().convert(null)).isEmpty();
- }
-
- @Test
- void testConvert_emptyString_returnsEmpty() {
- assertThat(FormField.named("lol").build().convert("")).hasValue("");
- }
-
- @Test
- void testWithDefault_hasValue_returnsValue() {
- assertThat(FormField.named("lol").withDefault("default").build().convert("return me!"))
- .hasValue("return me!");
- }
-
- @Test
- void testWithDefault_nullValue_returnsDefault() {
- assertThat(FormField.named("lol").withDefault("default").build().convert(null))
- .hasValue("default");
- }
-
- @Test
- void testEmptyToNull_emptyString_notPresent() {
- assertThat(FormField.named("lol").emptyToNull().build().convert("")).isEmpty();
- }
-
- @Test
- void testEmptyToNullRequired_emptyString_throwsFfe() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () -> FormField.named("lol").emptyToNull().required().build().convert(""));
- assertThat(thrown, equalTo(new FormFieldException("This field is required.").propagate("lol")));
- }
-
- @Test
- void testEmptyToNull_typeMismatch() {
- assertThrows(
- IllegalStateException.class, () -> FormField.named("lol", Object.class).emptyToNull());
- }
-
- @Test
- void testNamedLong() {
- assertThat(FormField.named("lol", Long.class).build().convert(666L)).hasValue(666L);
- }
-
- @Test
- void testUppercased() {
- FormField field = FormField.named("lol").uppercased().build();
- assertThat(field.convert(null)).isEmpty();
- assertThat(field.convert("foo")).hasValue("FOO");
- assertThat(field.convert("BAR")).hasValue("BAR");
- }
-
- @Test
- void testLowercased() {
- FormField field = FormField.named("lol").lowercased().build();
- assertThat(field.convert(null)).isEmpty();
- assertThat(field.convert("foo")).hasValue("foo");
- assertThat(field.convert("BAR")).hasValue("bar");
- }
-
- @Test
- void testIn_passesThroughNull() {
- FormField field =
- FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build();
- assertThat(field.convert(null)).isEmpty();
- }
-
- @Test
- void testIn_valueIsContainedInSet() {
- FormField field =
- FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build();
- assertThat(field.convert("foo")).hasValue("foo");
- assertThat(field.convert("bar")).hasValue("bar");
- }
-
- @Test
- void testIn_valueMissingFromSet() {
- FormField field =
- FormField.named("lol").in(ImmutableSet.of("foo", "bar")).build();
- FormFieldException thrown = assertThrows(FormFieldException.class, () -> field.convert("omfg"));
- assertThat(thrown, equalTo(new FormFieldException("Unrecognized value.").propagate("lol")));
- }
-
- @Test
- void testRange_hasLowerBound_nullValue_passesThrough() {
- assertThat(FormField.named("lol").range(atLeast(5)).build().convert(null)).isEmpty();
- }
-
- @Test
- void testRange_minimum_stringLengthEqualToMinimum_doesNothing() {
- assertThat(FormField.named("lol").range(atLeast(5)).build().convert("hello")).hasValue("hello");
- }
-
- @Test
- void testRange_minimum_stringLengthShorterThanMinimum_throwsFfe() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () -> FormField.named("lol").range(atLeast(4)).build().convert("lol"));
- assertThat(thrown).hasMessageThat().contains("Number of characters (3) not in range [4");
- }
-
- @Test
- void testRange_noLowerBound_nullValue_passThrough() {
- assertThat(FormField.named("lol").range(atMost(5)).build().convert(null)).isEmpty();
- }
-
- @Test
- void testRange_maximum_stringLengthEqualToMaximum_doesNothing() {
- assertThat(FormField.named("lol").range(atMost(5)).build().convert("hello")).hasValue("hello");
- }
-
- @Test
- void testRange_maximum_stringLengthShorterThanMaximum_throwsFfe() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () -> FormField.named("lol").range(atMost(5)).build().convert("omgomg"));
- assertThat(thrown).hasMessageThat().contains("Number of characters (6) not in range");
- }
-
- @Test
- void testRange_numericTypes() {
- FormField.named("lol", Byte.class).range(closed(5, 10)).build().convert((byte) 7);
- FormField.named("lol", Short.class).range(closed(5, 10)).build().convert((short) 7);
- FormField.named("lol", Integer.class).range(closed(5, 10)).build().convert(7);
- FormField.named("lol", Long.class).range(closed(5, 10)).build().convert(7L);
- FormField.named("lol", Float.class).range(closed(5, 10)).build().convert(7F);
- FormField.named("lol", Double.class).range(closed(5, 10)).build().convert(7D);
- }
-
- @Test
- void testRange_typeMismatch() {
- assertThrows(
- IllegalStateException.class, () -> FormField.named("lol", Object.class).range(atMost(5)));
- }
-
- @Test
- void testMatches_matches_doesNothing() {
- assertThat(FormField.named("lol").matches(Pattern.compile("[a-z]+")).build().convert("abc"))
- .hasValue("abc");
- }
-
- @Test
- void testMatches_mismatch_throwsFfeAndShowsDefaultErrorMessageWithPattern() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () ->
- FormField.named("lol")
- .matches(Pattern.compile("[a-z]+"))
- .build()
- .convert("123abc456"));
- assertThat(
- thrown, equalTo(new FormFieldException("Must match pattern: [a-z]+").propagate("lol")));
- }
-
- @Test
- void testMatches_typeMismatch() {
- assertThrows(
- IllegalStateException.class,
- () -> FormField.named("lol", Object.class).matches(Pattern.compile(".")));
- }
-
- @Test
- void testRetains() {
- assertThat(
- FormField.named("lol")
- .retains(CharMatcher.anyOf("0123456789"))
- .build()
- .convert(" 123 1593-43 453 45 4 4 \t"))
- .hasValue("1231593434534544");
- }
-
- @Test
- void testCast() {
- assertThat(
- FormField.named("lol")
- .transform(Integer.class, Integer::parseInt)
- .build()
- .convert("123"))
- .hasValue(123);
- }
-
- @Test
- void testCast_twice() {
- assertThat(
- FormField.named("lol")
- .transform(Object.class, Integer::parseInt)
- .transform(String.class, Object::toString)
- .build()
- .convert("123"))
- .hasValue("123");
- }
-
- @Test
- void testAsList_null_notPresent() {
- assertThat(FormField.named("lol").asList().build().convert(null)).isEmpty();
- }
-
- @Test
- void testAsList_empty_returnsEmpty() {
- assertThat(FormField.named("lol").asList().build().convert(ImmutableList.of()))
- .hasValue(ImmutableList.of());
- }
-
- @Test
- void testAsListEmptyToNullRequired_empty_throwsFfe() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () ->
- FormField.named("lol")
- .asList()
- .emptyToNull()
- .required()
- .build()
- .convert(ImmutableList.of()));
- assertThat(thrown, equalTo(new FormFieldException("This field is required.").propagate("lol")));
- }
-
- @Test
- void testListEmptyToNull_empty_notPresent() {
- assertThat(FormField.named("lol").asList().emptyToNull().build().convert(ImmutableList.of()))
- .isEmpty();
- }
-
- @Test
- void testAsEnum() {
- FormField omgField =
- FormField.named("omg").asEnum(ICanHazEnum.class).build();
- assertThat(omgField.convert("LOL")).hasValue(ICanHazEnum.LOL);
- assertThat(omgField.convert("CAT")).hasValue(ICanHazEnum.CAT);
- }
-
- @Test
- void testAsEnum_lowercase_works() {
- FormField omgField =
- FormField.named("omg").asEnum(ICanHazEnum.class).build();
- assertThat(omgField.convert("lol")).hasValue(ICanHazEnum.LOL);
- assertThat(omgField.convert("cat")).hasValue(ICanHazEnum.CAT);
- }
-
- @Test
- void testAsEnum_badInput_throwsFfe() {
- FormField omgField =
- FormField.named("omg").asEnum(ICanHazEnum.class).build();
- FormFieldException thrown =
- assertThrows(FormFieldException.class, () -> omgField.convert("helo"));
- assertThat(
- thrown,
- equalTo(
- new FormFieldException("Enum ICanHazEnum does not contain 'helo'").propagate("omg")));
- }
-
- @Test
- void testSplitList() {
- FormField> field =
- FormField.named("lol").asList(Splitter.on(',').omitEmptyStrings()).build();
- assertThat(field.convert("oh,my,goth").get()).containsExactly("oh", "my", "goth").inOrder();
- assertThat(field.convert("").get()).isEmpty();
- assertThat(field.convert(null)).isEmpty();
- }
-
- @Test
- void testSplitSet() {
- FormField> field =
- FormField.named("lol").uppercased().asSet(Splitter.on(',').omitEmptyStrings()).build();
- assertThat(field.convert("oh,my,goth").get()).containsExactly("OH", "MY", "GOTH").inOrder();
- assertThat(field.convert("").get()).isEmpty();
- assertThat(field.convert(null)).isEmpty();
- }
-
- @Test
- void testAsList() {
- assertThat(
- FormField.named("lol")
- .asList()
- .build()
- .convert(ImmutableList.of("lol", "cat", ""))
- .get())
- .containsExactly("lol", "cat", "")
- .inOrder();
- }
-
- @Test
- void testAsList_trimmedEmptyToNullOnItems() {
- assertThat(
- FormField.named("lol")
- .trimmed()
- .emptyToNull()
- .matches(Pattern.compile("[a-z]+"))
- .asList()
- .range(closed(1, 2))
- .build()
- .convert(ImmutableList.of("lol\n", "\tcat "))
- .get())
- .containsExactly("lol", "cat")
- .inOrder();
- }
-
- @Test
- void testAsList_nullElements_getIgnored() {
- assertThat(
- FormField.named("lol")
- .emptyToNull()
- .asList()
- .build()
- .convert(ImmutableList.of("omg", ""))
- .get())
- .containsExactly("omg");
- }
-
- @Test
- void testAsListRequiredElements_nullElement_throwsFfeWithIndex() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () ->
- FormField.named("lol")
- .emptyToNull()
- .required()
- .asList()
- .build()
- .convert(ImmutableList.of("omg", "")));
- assertThat(
- thrown,
- equalTo(new FormFieldException("This field is required.").propagate(1).propagate("lol")));
- }
-
- @Test
- void testMapAsListRequiredElements_nullElement_throwsFfeWithIndexAndKey() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () ->
- FormField.mapNamed("lol")
- .transform(
- String.class,
- input ->
- FormField.named("cat")
- .emptyToNull()
- .required()
- .build()
- .extractUntyped(input)
- .get())
- .asList()
- .build()
- .convert(ImmutableList.of(ImmutableMap.of("cat", ""))));
- assertThat(
- thrown,
- equalTo(
- new FormFieldException("This field is required.")
- .propagate("cat")
- .propagate(0)
- .propagate("lol")));
- }
-
- @Test
- void testAsListTrimmed_typeMismatch() {
- FormField.named("lol").trimmed().asList();
- assertThrows(IllegalStateException.class, () -> FormField.named("lol").asList().trimmed());
- }
-
- @Test
- void testAsMatrix() {
- assertThat(
- FormField.named("lol", Integer.class)
- .transform(input -> input * 2)
- .asList()
- .asList()
- .build()
- .convert(
- Lists.cartesianProduct(
- ImmutableList.of(ImmutableList.of(1, 2), ImmutableList.of(3, 4))))
- .get())
- .containsExactly(
- ImmutableList.of(2, 6),
- ImmutableList.of(2, 8),
- ImmutableList.of(4, 6),
- ImmutableList.of(4, 8))
- .inOrder();
- }
-
- @Test
- void testAsSet() {
- assertThat(
- FormField.named("lol")
- .asSet()
- .build()
- .convert(ImmutableList.of("lol", "cat", "cat"))
- .get())
- .containsExactly("lol", "cat");
- }
-
- @Test
- void testTrimmed() {
- assertThat(FormField.named("lol").trimmed().build().convert(" \thello \t\n")).hasValue("hello");
- }
-
- @Test
- void testTrimmed_typeMismatch() {
- assertThrows(IllegalStateException.class, () -> FormField.named("lol", Object.class).trimmed());
- }
-
- @Test
- void testAsBuilder() {
- FormField field = FormField.named("omg").uppercased().build();
- assertThat(field.name()).isEqualTo("omg");
- assertThat(field.convert("hello")).hasValue("HELLO");
- field = field.asBuilder().build();
- assertThat(field.name()).isEqualTo("omg");
- assertThat(field.convert("hello")).hasValue("HELLO");
- }
-
- @Test
- void testAsBuilderNamed() {
- FormField field = FormField.named("omg").uppercased().build();
- assertThat(field.name()).isEqualTo("omg");
- assertThat(field.convert("hello")).hasValue("HELLO");
- field = field.asBuilderNamed("bog").build();
- assertThat(field.name()).isEqualTo("bog");
- assertThat(field.convert("hello")).hasValue("HELLO");
- }
-
- @Test
- void testNullness() {
- NullPointerTester tester =
- new NullPointerTester()
- .setDefault(Class.class, Object.class)
- .setDefault(Function.class, x -> x)
- .setDefault(Pattern.class, Pattern.compile("."))
- .setDefault(String.class, "hello.com");
- tester.testAllPublicStaticMethods(FormField.class);
- tester.testAllPublicInstanceMethods(FormField.named("lol"));
- tester.testAllPublicInstanceMethods(FormField.named("lol").build());
- }
-}
diff --git a/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java b/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java
deleted file mode 100644
index 6fceb1e21..000000000
--- a/core/src/test/java/google/registry/ui/forms/FormFieldsTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.ui.forms;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import com.google.common.testing.NullPointerTester;
-import org.junit.jupiter.api.Test;
-
-/** Unit tests for {@link FormFields}. */
-class FormFieldsTest {
-
- @Test
- void testXsToken_collapsesAndTrimsWhitespace() {
- assertThat(FormFields.XS_TOKEN.convert(" hello \r\n\t there\n")).hasValue("hello there");
- }
-
- @Test
- void testXsNormalizedString_extraSpaces_doesntCare() {
- assertThat(FormFields.XS_NORMALIZED_STRING.convert("hello there")).hasValue("hello there");
- }
-
- @Test
- void testXsNormalizedString_sideSpaces_doesntCare() {
- assertThat(FormFields.XS_NORMALIZED_STRING.convert(" hello there ")).hasValue(" hello there ");
- }
-
- @Test
- void testXsNormalizedString_containsNonSpaceWhitespace_fails() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class,
- () -> FormFields.XS_NORMALIZED_STRING.convert(" hello \r\n\t there\n"));
- assertThat(
- thrown,
- equalTo(
- new FormFieldException("Must not contain tabs or multiple lines.")
- .propagate("xsNormalizedString")));
- }
-
- @Test
- void testXsEppE164PhoneNumber_nanpaNumber_validates() {
- assertThat(FormFields.XS_NORMALIZED_STRING.convert("+1.2125650000")).hasValue("+1.2125650000");
- }
-
- @Test
- void testXsEppE164PhoneNumber_londonNumber_validates() {
- assertThat(FormFields.XS_NORMALIZED_STRING.convert("+44.2011112222"))
- .hasValue("+44.2011112222");
- }
-
- @Test
- void testXsEppE164PhoneNumber_localizedNumber_fails() {
- FormFieldException thrown =
- assertThrows(
- FormFieldException.class, () -> FormFields.PHONE_NUMBER.convert("(212) 565-0000"));
- assertThat(thrown)
- .hasMessageThat()
- .contains("Must be a valid +E.164 phone number, e.g. +1.2125650000");
- }
-
- @Test
- void testXsEppE164PhoneNumber_appliesXsTokenTransform() {
- assertThat(FormFields.PHONE_NUMBER.convert(" +1.2125650000 \r")).hasValue("+1.2125650000");
- }
-
- @Test
- void testXsEppRoid_correctSyntax_validates() {
- assertThat(FormFields.ROID.convert("SH8013-REP")).hasValue("SH8013-REP");
- }
-
- @Test
- void testXsEppRoid_lowerCase_validates() {
- assertThat(FormFields.ROID.convert("sh8013-rep")).hasValue("sh8013-rep");
- }
-
- @Test
- void testXsEppRoid_missingHyphen_fails() {
- FormFieldException thrown =
- assertThrows(FormFieldException.class, () -> FormFields.ROID.convert("SH8013REP"));
- assertThat(thrown).hasMessageThat().contains("Please enter a valid EPP ROID, e.g. SH8013-REP");
- }
-
- @Test
- void testXsEppRoid_appliesXsTokenTransform() {
- assertThat(FormFields.ROID.convert("\n FOO-BAR \r")).hasValue("FOO-BAR");
- }
-
- @Test
- void testNullness() {
- new NullPointerTester().testAllPublicStaticMethods(FormFields.class);
- }
-}