diff --git a/java/google/registry/model/reporting/DomainTransactionRecord.java b/java/google/registry/model/reporting/DomainTransactionRecord.java new file mode 100644 index 000000000..87c7b922e --- /dev/null +++ b/java/google/registry/model/reporting/DomainTransactionRecord.java @@ -0,0 +1,175 @@ +// 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.reporting; + +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; + +import com.google.common.collect.ImmutableSet; +import com.googlecode.objectify.annotation.Embed; +import google.registry.model.Buildable; +import google.registry.model.ImmutableObject; +import java.util.Set; +import org.joda.time.DateTime; + +/** + * The record of the mutations which contribute to transaction reporting. + * + *
This will only be constructed for a HistoryEntry which contributes to the transaction report, + * i.e. only domain mutations. + * + *
The registrar accredited with this transaction is the enclosing HistoryEntry.clientId. The + * only exception is for reportField = TRANSFER_LOSING_SUCCESSFUL or TRANSFER_LOSING_NACKED, which + * uses HistoryEntry.otherClientId because the losing party in a transfer is always the otherClient. + */ +@Embed +public class DomainTransactionRecord extends ImmutableObject implements Buildable { + + /** + * The time this Transaction takes effect (counting grace periods and other nuances). + * + *
Net adds, renews and transfers are modificationTime + 5 days for the grace period, while
+ * Autorenews have a 45 day grace period. For deletions, this is the purge date of the domain. And
+ * for restored names, this is the modificationTime, if done in the 30 day redemption period.
+ *
+ * @see
+ * Grace period spec
+ */
+ DateTime reportingTime;
+
+ /** The TLD this record operates on. */
+ String tld;
+
+ /** The fields affected by this transaction, and the amounts they're affected by. */
+ Set For adds, renews, deletes, and restores, this is +1. For their respective cancellations,
+ * this is -1.
+ *
+ * For transfers, the gaining party gets a +1 for TRANSFER_GAINING_SUCCESSFUL, whereas the
+ * losing party gets a +1 for TRANSFER_LOSING_SUCCESSFUL. Nacks result in +1 for
+ * TRANSFER_GAINING_NACKED and TRANSFER_LOSING_NACKED, as well as -1 entries to cancel out the
+ * original SUCCESSFUL transfer counters. Finally, if we explicitly allow a transfer, the report
+ * amount is 0, as we've already counted the transfer in the original request.
+ */
+ int reportAmount;
+
+ /**
+ * The field added to by reportAmount within the transaction report.
+ *
+ * The reportField specifies which column the reportAmount contributes to in the overall
+ * report. ICANN wants a column for every add/renew broken down by number of years, so we have
+ * the NET_ADDS_#_YR and NET_RENEWS_#_YR boilerplate to facilitate report generation.
+ */
+ public enum TransactionReportField {
+ NET_ADDS_1_YR,
+ NET_ADDS_2_YR,
+ NET_ADDS_3_YR,
+ NET_ADDS_4_YR,
+ NET_ADDS_5_YR,
+ NET_ADDS_6_YR,
+ NET_ADDS_7_YR,
+ NET_ADDS_8_YR,
+ NET_ADDS_9_YR,
+ NET_ADDS_10_YR,
+ NET_RENEWS_1_YR,
+ NET_RENEWS_2_YR,
+ NET_RENEWS_3_YR,
+ NET_RENEWS_4_YR,
+ NET_RENEWS_5_YR,
+ NET_RENEWS_6_YR,
+ NET_RENEWS_7_YR,
+ NET_RENEWS_8_YR,
+ NET_RENEWS_9_YR,
+ NET_RENEWS_10_YR,
+ TRANSFER_GAINING_SUCCESSFUL,
+ TRANSFER_GAINING_NACKED,
+ TRANSFER_LOSING_SUCCESSFUL,
+ TRANSFER_LOSING_NACKED,
+ DELETED_DOMAINS_GRACE,
+ DELETED_DOMAINS_NOGRACE,
+ RESTORED_DOMAINS
+ }
+ }
+
+ public DateTime getReportingTime() {
+ return reportingTime;
+ }
+
+ public String getTld() {
+ return tld;
+ }
+
+ @Override
+ public Builder asBuilder() {
+ return new Builder(clone(this));
+ }
+
+ /** A builder for {@link DomainTransactionRecord} since it is immutable. */
+ public static class Builder extends Buildable.Builder This will be null for any HistoryEntry generated before this field was added. This will
+ * also be null if the HistoryEntry refers to an EPP mutation that does not affect domain
+ * transaction counts (such as contact or host mutations).
+ */
+ @Nullable
+ DomainTransactionRecord domainTransactionRecord;
+
public Key extends EppResource> getParent() {
return parent;
}
@@ -168,6 +179,11 @@ public class HistoryEntry extends ImmutableObject implements Buildable {
return requestedByRegistrar;
}
+ @Nullable
+ public DomainTransactionRecord getDomainTransactionRecord() {
+ return domainTransactionRecord;
+ }
+
@Override
public Builder asBuilder() {
return new Builder(clone(this));
@@ -240,5 +256,10 @@ public class HistoryEntry extends ImmutableObject implements Buildable {
getInstance().requestedByRegistrar = requestedByRegistrar;
return this;
}
+
+ public Builder setDomainTransactionRecord(DomainTransactionRecord domainTransactionRecord) {
+ getInstance().domainTransactionRecord = domainTransactionRecord;
+ return this;
+ }
}
}
diff --git a/javatests/google/registry/model/reporting/HistoryEntryTest.java b/javatests/google/registry/model/reporting/HistoryEntryTest.java
index 1aa64e30c..de782007d 100644
--- a/javatests/google/registry/model/reporting/HistoryEntryTest.java
+++ b/javatests/google/registry/model/reporting/HistoryEntryTest.java
@@ -16,14 +16,17 @@ package google.registry.model.reporting;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
+import static google.registry.model.reporting.DomainTransactionRecord.TransactionFieldAmount.TransactionReportField.NET_ADDS_1_YR;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newDomainResource;
import static google.registry.testing.DatastoreHelper.persistResource;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.common.collect.ImmutableSet;
import google.registry.model.EntityTestCase;
import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
+import google.registry.model.reporting.DomainTransactionRecord.TransactionFieldAmount;
import org.junit.Before;
import org.junit.Test;
@@ -35,22 +38,30 @@ public class HistoryEntryTest extends EntityTestCase {
@Before
public void setUp() throws Exception {
createTld("foobar");
+ DomainTransactionRecord transactionRecord =
+ new DomainTransactionRecord.Builder()
+ .setTld("foobar")
+ .setReportingTime(clock.nowUtc())
+ .setTransactionFieldAmounts(
+ ImmutableSet.of(TransactionFieldAmount.create(NET_ADDS_1_YR, 1)))
+ .build();
// Set up a new persisted HistoryEntry entity.
- historyEntry = new HistoryEntry.Builder()
- .setParent(newDomainResource("foo.foobar"))
- .setType(HistoryEntry.Type.DOMAIN_CREATE)
- .setPeriod(Period.create(1, Period.Unit.YEARS))
- .setXmlBytes("