1
0
mirror of https://github.com/google/nomulus synced 2026-07-22 07:52:22 +00:00

Run Address validation on GSON deserialization too (#3166)

we should run these checks when we deserialize from json objects, not
just when we use the builder to construct it. We did something similar
for TLD recently.
This commit is contained in:
gbrodman
2026-07-21 10:00:36 -04:00
committed by GitHub
parent a2f0035f89
commit 718ed29070
4 changed files with 157 additions and 18 deletions
@@ -155,6 +155,7 @@ public class Address extends ImmutableObject
@Override
public void postProcess() {
validateState();
if (street == null || street.isEmpty()) {
return;
}
@@ -163,6 +164,21 @@ public class Address extends ImmutableObject
streetLine3 = street.size() >= 3 ? street.get(2) : null;
}
public void validateState() {
checkArgument(
street == null || (!street.isEmpty() && street.size() <= 3),
"Street address must have [1-3] lines: %s",
street);
//noinspection ConstantConditions
checkArgument(
street == null || street.stream().noneMatch(String::isEmpty),
"Street address cannot contain empty string: %s",
street);
checkArgument(
countryCode == null || countryCode.length() == 2,
"Country code should be a 2 character string");
}
/** A builder for constructing {@link Address}. */
public static class Builder<T extends Address> extends Buildable.Builder<T> {
@@ -172,16 +188,13 @@ public class Address extends ImmutableObject
super(instance);
}
@Override
public T build() {
getInstance().validateState();
return super.build();
}
public Builder<T> setStreet(ImmutableList<String> street) {
checkArgument(
street == null || (!street.isEmpty() && street.size() <= 3),
"Street address must have [1-3] lines: %s",
street);
//noinspection ConstantConditions
checkArgument(
street.stream().noneMatch(String::isEmpty),
"Street address cannot contain empty string: %s",
street);
getInstance().street = street;
getInstance().streetLine1 = street.get(0);
getInstance().streetLine2 = street.size() >= 2 ? street.get(1) : null;
@@ -37,6 +37,14 @@ public class RegistrarAddress extends Address {
return new Builder(clone(this));
}
@Override
public void validateState() {
super.validateState();
checkNotNull(forceEmptyToNull(getStreet()), "Missing street");
checkNotNull(getCity(), "Missing city");
checkNotNull(getCountryCode(), "Missing country code");
}
/** Builder for {@link RegistrarAddress}. */
public static class Builder extends Address.Builder<RegistrarAddress> {
public Builder() {}
@@ -44,14 +52,5 @@ public class RegistrarAddress extends Address {
private Builder(RegistrarAddress instance) {
super(instance);
}
@Override
public RegistrarAddress build() {
RegistrarAddress instance = getInstance();
checkNotNull(forceEmptyToNull(instance.getStreet()), "Missing street");
checkNotNull(instance.getCity(), "Missing city");
checkNotNull(instance.getCountryCode(), "Missing country code");
return super.build();
}
}
}
@@ -25,6 +25,7 @@ import google.registry.model.ImmutableObject;
import google.registry.model.eppcommon.AddressTest.TestEntity.TestAddress;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.tools.GsonUtils;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@@ -124,6 +125,38 @@ class AddressTest {
assertAddress(unmarshalledEntity.address, "123 W 14th St", "8th Fl", "Rm 8");
}
@Test
void testSuccess_gsonInstantiation() {
String json = "{\"street\":[\"123 W 14th St\",\"8th Fl\"],\"countryCode\":\"US\"}";
TestAddress address = GsonUtils.provideGson().fromJson(json, TestAddress.class);
assertThat(address.getStreet()).containsExactly("123 W 14th St", "8th Fl");
assertThat(address.getCountryCode()).isEqualTo("US");
}
@Test
void testFailure_gsonInstantiation_tooManyStreetLines() {
String json = "{\"street\":[\"123 W 14th St\",\"8th Fl\",\"Rm 8\",\"4th Line\"]}";
assertThrows(
IllegalArgumentException.class,
() -> GsonUtils.provideGson().fromJson(json, TestAddress.class));
}
@Test
void testFailure_gsonInstantiation_emptyStreetLine() {
String json = "{\"street\":[\"123 W 14th St\",\"\"]}";
assertThrows(
IllegalArgumentException.class,
() -> GsonUtils.provideGson().fromJson(json, TestAddress.class));
}
@Test
void testFailure_gsonInstantiation_invalidCountryCode() {
String json = "{\"countryCode\":\"USA\"}";
assertThrows(
IllegalArgumentException.class,
() -> GsonUtils.provideGson().fromJson(json, TestAddress.class));
}
private static TestAddress createAddress(String... streetList) {
return new TestAddress.Builder()
.setStreet(ImmutableList.copyOf(streetList))
@@ -0,0 +1,94 @@
// Copyright 2026 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.model.registrar;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import google.registry.tools.GsonUtils;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link RegistrarAddress}. */
class RegistrarAddressTest {
@Test
void testSuccess_gsonInstantiation() {
String json = "{\"street\":[\"123 W 14th St\"],\"city\":\"New York\",\"countryCode\":\"US\"}";
RegistrarAddress address = GsonUtils.provideGson().fromJson(json, RegistrarAddress.class);
assertThat(address.getStreet()).containsExactly("123 W 14th St");
assertThat(address.getCity()).isEqualTo("New York");
assertThat(address.getCountryCode()).isEqualTo("US");
}
@Test
void testFailure_gsonInstantiation_missingStreet() {
String json = "{\"city\":\"New York\",\"countryCode\":\"US\"}";
assertThrows(
NullPointerException.class,
() -> GsonUtils.provideGson().fromJson(json, RegistrarAddress.class));
}
@Test
void testFailure_gsonInstantiation_missingCity() {
String json = "{\"street\":[\"123 W 14th St\"],\"countryCode\":\"US\"}";
assertThrows(
NullPointerException.class,
() -> GsonUtils.provideGson().fromJson(json, RegistrarAddress.class));
}
@Test
void testFailure_gsonInstantiation_missingCountryCode() {
String json = "{\"street\":[\"123 W 14th St\"],\"city\":\"New York\"}";
assertThrows(
NullPointerException.class,
() -> GsonUtils.provideGson().fromJson(json, RegistrarAddress.class));
}
@Test
void testFailure_gsonInstantiation_invalidCountryCode() {
String json = "{\"street\":[\"123 W 14th St\"],\"city\":\"New York\",\"countryCode\":\"USA\"}";
assertThrows(
IllegalArgumentException.class,
() -> GsonUtils.provideGson().fromJson(json, RegistrarAddress.class));
}
@Test
void testFailure_gsonInstantiation_tooManyStreetLines() {
String json =
"{\"street\":[\"1\",\"2\",\"3\",\"4\"],\"city\":\"New York\",\"countryCode\":\"US\"}";
assertThrows(
IllegalArgumentException.class,
() -> GsonUtils.provideGson().fromJson(json, RegistrarAddress.class));
}
@Test
void testSuccess_builder() {
RegistrarAddress address =
new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("123 W 14th St"))
.setCity("New York")
.setCountryCode("US")
.build();
assertThat(address.getStreet()).containsExactly("123 W 14th St");
}
@Test
void testFailure_builder_missingStreet() {
assertThrows(
NullPointerException.class,
() -> new RegistrarAddress.Builder().setCity("New York").setCountryCode("US").build());
}
}