Update WHOIS output format to reflect CL&D and PDT requirements

PDT testing revealed a couple ways in which our WHOIS output was non-compliant. First, the Consistent Labeling & Display policy dictates that the contact IDs must be ROIDs. See rule 11 in https://www.icann.org/resources/pages/rdds-labeling-policy-2017-02-01-en. Second, PDT tests expect that a WHOIS response will treat missing values either by omitting the line entirely, or by including the line with a blank value, but not both. So this is legal:

Phone Number: 123-4567
Phone Number Ext:
Fax Number: 123-4568
Fax Number Ext:

and this is legal:

Phone Number: 123-4567
Fax Number: 123-4568

but this is not:

Phone Number: 123-4567
Phone Number Ext:
Fax Number: 123-4568

In the last example, one extension line is present with a blank value, while the other extension line is omitted. We cannot do both. Therefore, we should update our code to omit lines with no value. Since we can't guarantee that we will always emit all lines that the parse might expect to see, it is safe to use the policy of omitting lines with no value.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=158184150
This commit is contained in:
mountford 2017-06-06 13:48:43 -07:00 committed by Ben McIlwain
parent ae039aa0d8
commit e8eabe01cf
6 changed files with 47 additions and 48 deletions

View file

@ -23,6 +23,7 @@ import static google.registry.xml.UtcDateTimeAdapter.getFormattedString;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.googlecode.objectify.Key;
@ -88,7 +89,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
.emitField("Registry Domain ID", domain.getRepoId())
.emitField("Registrar WHOIS Server", registrar.getWhoisServer())
.emitField("Registrar URL", registrar.getReferralUrl())
.emitField("Updated Date", getFormattedString(domain.getLastEppUpdateTime()))
.emitFieldIfDefined("Updated Date", getFormattedString(domain.getLastEppUpdateTime()))
.emitField("Creation Date", getFormattedString(domain.getCreationTime()))
.emitField(
"Registry Expiry Date", getFormattedString(domain.getRegistrationExpirationTime()))
@ -98,10 +99,10 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
Objects.toString(registrar.getIanaIdentifier(), ""))
// Email address is a required field for registrar contacts. Therefore as long as there
// is an abuse contact, we can get an email address from it.
.emitField(
.emitFieldIfDefined(
"Registrar Abuse Contact Email",
abuseContact.isPresent() ? abuseContact.get().getEmailAddress() : null)
.emitField(
.emitFieldIfDefined(
"Registrar Abuse Contact Phone",
abuseContact.isPresent() ? abuseContact.get().getPhoneNumber() : null)
.emitStatusValues(domain.getStatusValues(), domain.getGracePeriods())
@ -144,10 +145,12 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
class DomainEmitter extends Emitter<DomainEmitter> {
DomainEmitter emitPhone(
String contactType, String title, @Nullable ContactPhoneNumber phoneNumber) {
return emitField(
contactType, title, phoneNumber != null ? phoneNumber.getPhoneNumber() : null)
.emitField(
contactType, title, "Ext", phoneNumber != null ? phoneNumber.getExtension() : null);
if (phoneNumber == null) {
return this;
}
return emitFieldIfDefined(ImmutableList.of(contactType, title), phoneNumber.getPhoneNumber())
.emitFieldIfDefined(
ImmutableList.of(contactType, title, "Ext"), phoneNumber.getExtension());
}
/** Emit the contact entry of the given type. */
@ -167,19 +170,20 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
domain.getFullyQualifiedDomainName(), contact);
return this;
}
emitField("Registry", contactType, "ID", contactResource.getContactId());
// ICANN Consistent Labeling & Display policy requires that this be the ROID.
emitField(ImmutableList.of("Registry", contactType, "ID"), contactResource.getRepoId());
PostalInfo postalInfo = chooseByUnicodePreference(
preferUnicode,
contactResource.getLocalizedPostalInfo(),
contactResource.getInternationalizedPostalInfo());
if (postalInfo != null) {
emitField(contactType, "Name", postalInfo.getName());
emitField(contactType, "Organization", postalInfo.getOrg());
emitFieldIfDefined(ImmutableList.of(contactType, "Name"), postalInfo.getName());
emitFieldIfDefined(ImmutableList.of(contactType, "Organization"), postalInfo.getOrg());
emitAddress(contactType, postalInfo.getAddress());
}
return emitPhone(contactType, "Phone", contactResource.getVoiceNumber())
.emitPhone(contactType, "Fax", contactResource.getFaxNumber())
.emitField(contactType, "Email", contactResource.getEmailAddress());
.emitField(ImmutableList.of(contactType, "Email"), contactResource.getEmailAddress());
}
/** Emits status values and grace periods as a set, in the AWIP format. */

View file

@ -22,12 +22,10 @@ import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import google.registry.model.eppcommon.Address;
import google.registry.util.Idn;
import google.registry.xml.UtcDateTimeAdapter;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
@ -109,6 +107,16 @@ abstract class WhoisResponseImpl implements WhoisResponse {
return thisCastToDerived();
}
/** Emit the field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(String name, @Nullable String value) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
stringBuilder.append(cleanse(name)).append(':');
stringBuilder.append(' ').append(cleanse(value));
return emitNewline();
}
/** Emit the field name and value followed by a newline. */
E emitField(String name, @Nullable String value) {
stringBuilder.append(cleanse(name)).append(':');
@ -118,11 +126,17 @@ abstract class WhoisResponseImpl implements WhoisResponse {
return emitNewline();
}
/** Emit a multi-part field name and value followed by a newline, but only if a value exists. */
E emitFieldIfDefined(List<String> nameParts, String value) {
if (isNullOrEmpty(value)) {
return thisCastToDerived();
}
return emitField(nameParts, value);
}
/** Emit a multi-part field name and value followed by a newline. */
E emitField(String... namePartsAndValue) {
List<String> parts = Arrays.asList(namePartsAndValue);
return emitField(
Joiner.on(' ').join(parts.subList(0, parts.size() - 1)), Iterables.getLast(parts));
E emitField(List<String> nameParts, String value) {
return emitField(Joiner.on(' ').join(nameParts), value);
}
/** Emit a contact address. */