// 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.model.contact; import static com.google.common.base.Preconditions.checkArgument; import static google.registry.model.EppResourceUtils.projectResourceOntoBuilderAtTime; import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.condition.IfNull; import google.registry.model.EppResource; import google.registry.model.EppResource.ForeignKeyedEppResource; import google.registry.model.EppResource.ResourceWithTransferData; import google.registry.model.annotations.ExternalMessagingName; import google.registry.model.annotations.ReportedOn; import google.registry.model.contact.PostalInfo.Type; import google.registry.model.transfer.TransferData; import javax.xml.bind.annotation.XmlElement; import org.joda.time.DateTime; /** * A persistable contact resource including mutable and non-mutable fields. * * @see RFC 5733 */ @Cache(expirationSeconds = RECOMMENDED_MEMCACHE_EXPIRATION) @ReportedOn @Entity @ExternalMessagingName("contact") public class ContactResource extends EppResource implements ForeignKeyedEppResource, ResourceWithTransferData { /** * Unique identifier for this contact. * *
This is only unique in the sense that for any given lifetime specified as the time range * from (creationTime, deletionTime) there can only be one contact in Datastore with this id. * However, there can be many contacts with the same id and non-overlapping lifetimes. */ String contactId; /** * Localized postal info for the contact. All contained values must be representable in the 7-bit * US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) PostalInfo localizedPostalInfo; /** * Internationalized postal info for the contact. Personal info; cleared by * {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) PostalInfo internationalizedPostalInfo; /** * Contact name used for name searches. This is set automatically to be the internationalized * postal name, or if null, the localized postal name, or if that is null as well, null. Personal * info; cleared by {@link Builder#wipeOut}. */ @Index String searchName; /** Contact’s voice number. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) ContactPhoneNumber voice; /** Contact’s fax number. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) ContactPhoneNumber fax; /** Contact’s email address. Personal info; cleared by {@link Builder#wipeOut}. */ @IgnoreSave(IfNull.class) String email; /** Authorization info (aka transfer secret) of the contact. */ ContactAuthInfo authInfo; /** Data about any pending or past transfers on this contact. */ TransferData transferData; /** * The time that this resource was last transferred. * *
Can be null if the resource has never been transferred. */ DateTime lastTransferTime; // If any new fields are added which contain personal information, make sure they are cleared by // the wipeOut() function, so that data is not kept around for deleted contacts. /** Disclosure policy. */ Disclose disclose; public String getContactId() { return contactId; } public PostalInfo getLocalizedPostalInfo() { return localizedPostalInfo; } public PostalInfo getInternationalizedPostalInfo() { return internationalizedPostalInfo; } public ContactPhoneNumber getVoiceNumber() { return voice; } public ContactPhoneNumber getFaxNumber() { return fax; } public String getEmailAddress() { return email; } public ContactAuthInfo getAuthInfo() { return authInfo; } public Disclose getDisclose() { return disclose; } @Override public final TransferData getTransferData() { return Optional.fromNullable(transferData).or(TransferData.EMPTY); } @Override public DateTime getLastTransferTime() { return lastTransferTime; } @Override public String getForeignKey() { return contactId; } /** * Postal info for the contact. * *
The XML marshalling expects the {@link PostalInfo} objects in a list, but we can't actually
* persist them to Datastore that way because Objectify can't handle collections of embedded
* objects that themselves contain collections, and there's a list of streets inside. This method
* transforms the persisted format to the XML format for marshalling.
*/
@XmlElement(name = "postalInfo")
public ImmutableList This should be used when deleting a contact so that the soft-deleted entity doesn't
* contain information that the registrant requested to be deleted.
*/
public Builder wipeOut() {
setEmailAddress(null);
setFaxNumber(null);
setInternationalizedPostalInfo(null);
setLocalizedPostalInfo(null);
setVoiceNumber(null);
return this;
}
@Override
public ContactResource build() {
ContactResource instance = getInstance();
// If TransferData is totally empty, set it to null.
if (TransferData.EMPTY.equals(instance.transferData)) {
setTransferData(null);
}
// Set the searchName using the internationalized and localized postal info names.
if ((instance.internationalizedPostalInfo != null)
&& (instance.internationalizedPostalInfo.getName() != null)) {
instance.searchName = instance.internationalizedPostalInfo.getName();
} else if ((instance.localizedPostalInfo != null)
&& (instance.localizedPostalInfo.getName() != null)) {
instance.searchName = instance.localizedPostalInfo.getName();
} else {
instance.searchName = null;
}
return super.build();
}
}
}