Require currency specific billingAccountId for registrar

For the currency specific billing account ids to work properly, any REAL registrar that is allowed to register domains in a certain TLD must have a billing account id for the currency of that TLD.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=155864711
This commit is contained in:
jianglai 2017-05-12 07:27:05 -07:00 committed by Ben McIlwain
parent 6e6a340113
commit 35624ace08
3 changed files with 110 additions and 7 deletions

View file

@ -24,14 +24,19 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import google.registry.model.billing.RegistrarBillingUtils; import google.registry.model.billing.RegistrarBillingUtils;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.BillingMethod; import google.registry.model.registrar.Registrar.BillingMethod;
import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registry.Registry;
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap; import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
import google.registry.tools.params.OptionalLongParameter; import google.registry.tools.params.OptionalLongParameter;
import google.registry.tools.params.OptionalPhoneNumberParameter; import google.registry.tools.params.OptionalPhoneNumberParameter;
@ -44,6 +49,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit; import org.joda.money.CurrencyUnit;
import org.joda.money.Money; import org.joda.money.Money;
@ -383,12 +389,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
if (contactsRequireSyncing != null) { if (contactsRequireSyncing != null) {
builder.setContactsRequireSyncing(contactsRequireSyncing); builder.setContactsRequireSyncing(contactsRequireSyncing);
} }
// When creating a new REAL registrar or changing the type to REAL, a passcode is required.
// Leave existing REAL registrars alone.
if (Registrar.Type.REAL.equals(registrarType)
&& (oldRegistrar == null || oldRegistrar.getPhonePasscode() == null)) {
checkArgument(phonePasscode != null, "--passcode is required for REAL registrars.");
}
if (phonePasscode != null) { if (phonePasscode != null) {
builder.setPhonePasscode(phonePasscode); builder.setPhonePasscode(phonePasscode);
} }
@ -414,7 +414,37 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
} }
} }
stageEntityChange(oldRegistrar, builder.build()); Registrar newRegistrar = builder.build();
// Apply some extra validation when creating a new REAL registrar or changing the type of a
// registrar to REAL. Leave existing REAL registrars alone.
if (Registrar.Type.REAL.equals(registrarType)) {
// Require a phone passcode.
checkArgument(
newRegistrar.getPhonePasscode() != null, "--passcode is required for REAL registrars.");
// Check if registrar has billing account IDs for the currency of the TLDs that it is
// allowed to register.
ImmutableSet<CurrencyUnit> tldCurrencies =
FluentIterable.from(newRegistrar.getAllowedTlds())
.transform(
new Function<String, CurrencyUnit>() {
@Override
public CurrencyUnit apply(String tld) {
return Registry.get(tld).getCurrency();
}
})
.toSet();
Set<CurrencyUnit> currenciesWithoutBillingAccountId =
newRegistrar.getBillingAccountMap() == null
? tldCurrencies
: Sets.difference(tldCurrencies, newRegistrar.getBillingAccountMap().keySet());
checkArgument(
currenciesWithoutBillingAccountId.isEmpty(),
"Need billing account map entries for currencies: %s",
Joiner.on(' ').join(currenciesWithoutBillingAccountId));
}
stageEntityChange(oldRegistrar, newRegistrar);
} }
} }
} }

View file

@ -167,6 +167,7 @@ public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarC
"--registrar_type=REAL", "--registrar_type=REAL",
"--iana_id=8", "--iana_id=8",
"--allowed_tlds=xn--q9jyb4c,foobar", "--allowed_tlds=xn--q9jyb4c,foobar",
"--billing_account_map=USD=123abc",
"--passcode=01234", "--passcode=01234",
"--icann_referral_email=foo@bar.test", "--icann_referral_email=foo@bar.test",
"--street=\"123 Fake St\"", "--street=\"123 Fake St\"",
@ -431,6 +432,53 @@ public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarC
.containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz"); .containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz");
} }
@Test
public void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() throws Exception {
createTlds("foo");
thrown.expect(IllegalArgumentException.class, "USD");
runCommandForced(
"--name=blobio",
"--password=some_password",
"--registrar_type=REAL",
"--iana_id=8",
"--billing_account_map=JPY=789xyz",
"--allowed_tlds=foo",
"--passcode=01234",
"--icann_referral_email=foo@bar.test",
"--street=\"123 Fake St\"",
"--city Fakington",
"--state MA",
"--zip 00351",
"--cc US",
"clientz");
}
@Test
public void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
createTlds("foo");
runCommandForced(
"--name=blobio",
"--password=some_password",
"--registrar_type=TEST",
"--billing_account_map=JPY=789xyz",
"--allowed_tlds=foo",
"--passcode=01234",
"--icann_referral_email=foo@bar.test",
"--street=\"123 Fake St\"",
"--city Fakington",
"--state MA",
"--zip 00351",
"--cc US",
"clientz");
Registrar registrar = Registrar.loadByClientId("clientz");
assertThat(registrar).isNotNull();
assertThat(registrar.getBillingAccountMap())
.containsExactly(CurrencyUnit.JPY, "789xyz");
}
@Test @Test
public void testSuccess_streetAddress() throws Exception { public void testSuccess_streetAddress() throws Exception {
runCommandForced( runCommandForced(

View file

@ -212,6 +212,31 @@ public class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarC
.containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz"); .containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz");
} }
@Test
public void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() throws Exception {
createTlds("foo");
assertThat(loadByClientId("NewRegistrar").getBillingAccountMap()).isEmpty();
thrown.expect(IllegalArgumentException.class, "USD");
runCommand(
"--billing_account_map=JPY=789xyz",
"--allowed_tlds=foo",
"--force",
"--registrar_type=REAL",
"NewRegistrar");
}
public void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
createTlds("foo");
assertThat(loadByClientId("NewRegistrar").getBillingAccountMap()).isEmpty();
runCommand(
"--billing_account_map=JPY=789xyz",
"--allowed_tlds=foo",
"--force",
"NewRegistrar");
assertThat(loadByClientId("NewRegistrar").getBillingAccountMap())
.containsExactly(CurrencyUnit.JPY, "789xyz");
}
@Test @Test
public void testSuccess_changeBillingMethodToBraintreeWhenBalanceIsZero() throws Exception { public void testSuccess_changeBillingMethodToBraintreeWhenBalanceIsZero() throws Exception {
createTlds("xn--q9jyb4c"); createTlds("xn--q9jyb4c");