diff --git a/core/src/main/java/google/registry/bsa/persistence/BsaDomainRefresh.java b/core/src/main/java/google/registry/bsa/persistence/BsaDomainRefresh.java new file mode 100644 index 000000000..4f58c454a --- /dev/null +++ b/core/src/main/java/google/registry/bsa/persistence/BsaDomainRefresh.java @@ -0,0 +1,117 @@ +// Copyright 2023 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.bsa.persistence; + +import static google.registry.bsa.persistence.BsaDomainRefresh.Stage.MAKE_DIFF; + +import com.google.common.base.Objects; +import google.registry.model.CreateAutoTimestamp; +import google.registry.model.UpdateAutoTimestamp; +import google.registry.persistence.VKey; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import org.joda.time.DateTime; + +/** + * Records of completed and ongoing refresh actions, which recomputes the set of unblockable domains + * and reports changes to BSA. + * + *

The refresh action only handles registered and reserved domain names. Invalid names only + * change status when the IDN tables change, and will be handled by a separate tool when it happens. + */ +@Entity +public class BsaDomainRefresh { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long jobId; + + @Column(nullable = false) + CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); + + @Column(nullable = false) + UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null); + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + Stage stage = MAKE_DIFF; + + BsaDomainRefresh() {} + + long getJobId() { + return jobId; + } + + DateTime getCreationTime() { + return creationTime.getTimestamp(); + } + + /** + * Returns the starting time of this job as a string, which can be used as folder name on GCS when + * storing download data. + */ + public String getJobName() { + return "refresh-" + getCreationTime().toString(); + } + + public Stage getStage() { + return this.stage; + } + + BsaDomainRefresh setStage(Stage stage) { + this.stage = stage; + return this; + } + + VKey vKey() { + return vKey(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BsaDomainRefresh)) { + return false; + } + BsaDomainRefresh that = (BsaDomainRefresh) o; + return Objects.equal(jobId, that.jobId) + && Objects.equal(creationTime, that.creationTime) + && Objects.equal(updateTime, that.updateTime) + && stage == that.stage; + } + + @Override + public int hashCode() { + return Objects.hashCode(jobId, creationTime, updateTime, stage); + } + + static VKey vKey(BsaDomainRefresh bsaDomainRefresh) { + return VKey.create(BsaDomainRefresh.class, bsaDomainRefresh.jobId); + } + + enum Stage { + MAKE_DIFF, + APPLY_DIFF, + REPORT_REMOVALS, + REPORT_ADDITIONS; + } +} diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index ee3cfd10b..b5711d5e4 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -38,6 +38,7 @@ META-INF/orm.xml + google.registry.bsa.persistence.BsaDomainRefresh google.registry.bsa.persistence.BsaDownload google.registry.bsa.persistence.BsaLabel google.registry.bsa.persistence.BsaDomainInUse diff --git a/core/src/test/java/google/registry/bsa/persistence/BsaDomainRefreshTest.java b/core/src/test/java/google/registry/bsa/persistence/BsaDomainRefreshTest.java new file mode 100644 index 000000000..9d163b27c --- /dev/null +++ b/core/src/test/java/google/registry/bsa/persistence/BsaDomainRefreshTest.java @@ -0,0 +1,54 @@ +// Copyright 2023 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.bsa.persistence; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.bsa.persistence.BsaDomainRefresh.Stage.MAKE_DIFF; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static org.joda.time.DateTimeZone.UTC; + +import google.registry.persistence.transaction.JpaTestExtensions; +import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension; +import google.registry.testing.FakeClock; +import org.joda.time.DateTime; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** Unit test for {@link BsaDomainRefresh}. */ +public class BsaDomainRefreshTest { + + protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC)); + + @RegisterExtension + final JpaIntegrationWithCoverageExtension jpa = + new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension(); + + @Test + void saveJob() { + BsaDomainRefresh persisted = + tm().transact(() -> tm().getEntityManager().merge(new BsaDomainRefresh())); + assertThat(persisted.jobId).isNotNull(); + assertThat(persisted.creationTime.getTimestamp()).isEqualTo(fakeClock.nowUtc()); + assertThat(persisted.stage).isEqualTo(MAKE_DIFF); + } + + @Test + void loadJobByKey() { + BsaDomainRefresh persisted = + tm().transact(() -> tm().getEntityManager().merge(new BsaDomainRefresh())); + assertThat(tm().transact(() -> tm().loadByKey(BsaDomainRefresh.vKey(persisted)))) + .isEqualTo(persisted); + } +} diff --git a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java index 359e92bf4..b086e12ef 100644 --- a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java +++ b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java @@ -17,6 +17,7 @@ package google.registry.schema.integration; import static com.google.common.truth.Truth.assert_; import google.registry.bsa.persistence.BsaDomainInUseTest; +import google.registry.bsa.persistence.BsaDomainRefreshTest; import google.registry.bsa.persistence.BsaDownloadTest; import google.registry.bsa.persistence.BsaLabelTest; import google.registry.model.billing.BillingBaseTest; @@ -86,6 +87,7 @@ import org.junit.runner.RunWith; AllocationTokenTest.class, BillingBaseTest.class, BsaDomainInUseTest.class, + BsaDomainRefreshTest.class, BsaDownloadTest.class, BsaLabelTest.class, BulkPricingPackageTest.class, diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 8fdbba8d7..668cef949 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -93,6 +93,14 @@ primary key (label, tld) ); + create table "BsaDomainRefresh" ( + job_id bigserial not null, + creation_time timestamptz not null, + stage text not null, + update_timestamp timestamptz, + primary key (job_id) + ); + create table "BsaDownload" ( job_id bigserial not null, block_list_checksums text not null,