From 9be5091c84ca5e7c5fe122d112f68329c139e82e Mon Sep 17 00:00:00 2001 From: Shicong Huang Date: Tue, 26 Nov 2019 16:51:41 -0500 Subject: [PATCH] Add entity for reserved list (#381) This PR added the Cloud SQL entity for reserved list. --- .../registry/schema/tld/ReservedList.java | 147 ++++++++++++++++++ .../main/resources/META-INF/persistence.xml | 1 + .../registry/schema/tld/ReservedListTest.java | 53 +++++++ .../V10__create_reserved_list_and_entry.sql | 36 +++++ .../sql/schema/db-schema.sql.generated | 22 +++ .../resources/sql/schema/nomulus.golden.sql | 81 ++++++++++ 6 files changed, 340 insertions(+) create mode 100644 core/src/main/java/google/registry/schema/tld/ReservedList.java create mode 100644 core/src/test/java/google/registry/schema/tld/ReservedListTest.java create mode 100644 db/src/main/resources/sql/flyway/V10__create_reserved_list_and_entry.sql diff --git a/core/src/main/java/google/registry/schema/tld/ReservedList.java b/core/src/main/java/google/registry/schema/tld/ReservedList.java new file mode 100644 index 000000000..c3897d157 --- /dev/null +++ b/core/src/main/java/google/registry/schema/tld/ReservedList.java @@ -0,0 +1,147 @@ +// Copyright 2019 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.schema.tld; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.ImmutableMap; +import google.registry.model.CreateAutoTimestamp; +import google.registry.model.ImmutableObject; +import google.registry.model.registry.label.ReservationType; +import java.util.Map; +import javax.annotation.Nullable; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import org.joda.time.DateTime; + +/** + * A list of reserved domain labels that are blocked from being registered for various reasons. + * + *

Note that the primary key of this entity is {@link #revisionId}, which is auto-generated by + * the database. So, if a retry of insertion happens after the previous attempt unexpectedly + * succeeds, we will end up with having two exact same reserved lists that differ only by + * revisionId. This is fine though, because we only use the list with the highest revisionId. + */ +@Entity +@Table(indexes = {@Index(columnList = "name", name = "reservedlist_name_idx")}) +public class ReservedList extends ImmutableObject { + + @Column(nullable = false) + private String name; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long revisionId; + + @Column(nullable = false) + private CreateAutoTimestamp creationTimestamp = CreateAutoTimestamp.create(null); + + @Column(nullable = false) + private Boolean shouldPublish; + + @ElementCollection + @CollectionTable( + name = "ReservedEntry", + joinColumns = @JoinColumn(name = "revisionId", referencedColumnName = "revisionId")) + @MapKeyColumn(name = "domainLabel") + private Map labelsToReservations; + + @Embeddable + public static class ReservedEntry extends ImmutableObject { + @Column(nullable = false) + private ReservationType reservationType; + + @Column(nullable = true) + private String comment; + + private ReservedEntry(ReservationType reservationType, @Nullable String comment) { + this.reservationType = reservationType; + this.comment = comment; + } + + // Hibernate requires this default constructor. + private ReservedEntry() {} + + /** Constructs a {@link ReservedEntry} object. */ + public static ReservedEntry create(ReservationType reservationType, @Nullable String comment) { + return new ReservedEntry(reservationType, comment); + } + + /** Returns the reservation type for this entry. */ + public ReservationType getReservationType() { + return reservationType; + } + + /** Returns the comment for this entry. Retruns null if there is no comment. */ + public String getComment() { + return comment; + } + } + + private ReservedList( + String name, Boolean shouldPublish, Map labelsToReservations) { + this.name = name; + this.shouldPublish = shouldPublish; + this.labelsToReservations = labelsToReservations; + } + + // Hibernate requires this default constructor. + private ReservedList() {} + + /** Constructs a {@link ReservedList} object. */ + public static ReservedList create( + String name, Boolean shouldPublish, Map labelsToReservations) { + return new ReservedList(name, shouldPublish, labelsToReservations); + } + + /** Returns the name of the reserved list. */ + public String getName() { + return name; + } + + /** Returns the ID of this revision, or throws if null. */ + public Long getRevisionId() { + checkState( + revisionId != null, + "revisionId is null because this object has not been persisted to the database yet"); + return revisionId; + } + + /** Returns the creation time of this revision of the reserved list. */ + public DateTime getCreationTimestamp() { + return creationTimestamp.getTimestamp(); + } + + /** Returns a {@link Map} of domain labels to {@link ReservedEntry}. */ + public ImmutableMap getLabelsToReservations() { + return ImmutableMap.copyOf(labelsToReservations); + } + + /** Returns true if the reserved list should be published. */ + public Boolean getShouldPublish() { + return shouldPublish; + } +} diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index 10a931a02..47e866d8a 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -24,6 +24,7 @@ google.registry.schema.tmch.ClaimsList google.registry.model.transfer.BaseTransferObject google.registry.schema.tld.PremiumList + google.registry.schema.tld.ReservedList google.registry.model.domain.secdns.DelegationSignerData google.registry.model.domain.DesignatedContact google.registry.model.domain.DomainBase diff --git a/core/src/test/java/google/registry/schema/tld/ReservedListTest.java b/core/src/test/java/google/registry/schema/tld/ReservedListTest.java new file mode 100644 index 000000000..5d3643b37 --- /dev/null +++ b/core/src/test/java/google/registry/schema/tld/ReservedListTest.java @@ -0,0 +1,53 @@ +// Copyright 2019 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.schema.tld; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; +import google.registry.model.registry.label.ReservationType; +import google.registry.schema.tld.ReservedList.ReservedEntry; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link ReservedList} */ +@RunWith(JUnit4.class) +public class ReservedListTest { + + @Test + public void verifyConstructorAndGetters_workCorrectly() { + ReservedList reservedList = + ReservedList.create( + "app", + false, + ImmutableMap.of( + "book", + ReservedEntry.create(ReservationType.ALLOWED_IN_SUNRISE, null), + "music", + ReservedEntry.create( + ReservationType.RESERVED_FOR_ANCHOR_TENANT, "reserved for anchor tenant"))); + + assertThat(reservedList.getName()).isEqualTo("app"); + assertThat(reservedList.getShouldPublish()).isFalse(); + assertThat(reservedList.getLabelsToReservations()) + .containsExactly( + "book", + ReservedEntry.create(ReservationType.ALLOWED_IN_SUNRISE, null), + "music", + ReservedEntry.create( + ReservationType.RESERVED_FOR_ANCHOR_TENANT, "reserved for anchor tenant")); + } +} diff --git a/db/src/main/resources/sql/flyway/V10__create_reserved_list_and_entry.sql b/db/src/main/resources/sql/flyway/V10__create_reserved_list_and_entry.sql new file mode 100644 index 000000000..2fc45f712 --- /dev/null +++ b/db/src/main/resources/sql/flyway/V10__create_reserved_list_and_entry.sql @@ -0,0 +1,36 @@ +-- Copyright 2019 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. + + create table "ReservedEntry" ( + revision_id int8 not null, + comment text, + reservation_type int4 not null, + domain_label text not null, + primary key (revision_id, domain_label) + ); + + create table "ReservedList" ( + revision_id bigserial not null, + creation_timestamp timestamptz not null, + name text not null, + should_publish boolean not null, + primary key (revision_id) + ); + +create index reservedlist_name_idx on "ReservedList" (name); + + alter table if exists "ReservedEntry" + add constraint FKgq03rk0bt1hb915dnyvd3vnfc + foreign key (revision_id) + references "ReservedList"; 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 63bc7031c..d2bb8ddf3 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -152,6 +152,22 @@ primary key (revision_id) ); + create table "ReservedEntry" ( + revision_id int8 not null, + comment text, + reservation_type int4 not null, + domain_label text not null, + primary key (revision_id, domain_label) + ); + + create table "ReservedList" ( + revision_id bigserial not null, + creation_timestamp timestamptz not null, + name text not null, + should_publish boolean not null, + primary key (revision_id) + ); + alter table if exists "Domain_DelegationSignerData" add constraint UK_2yp55erx1i51pa7gnb8bu7tjn unique (ds_data_key_tag); @@ -166,6 +182,7 @@ create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id); alter table if exists "RegistryLock" add constraint idx_registry_lock_repo_id_revision_id unique (repo_id, revision_id); +create index reservedlist_name_idx on "ReservedList" (name); alter table if exists "ClaimsEntry" add constraint FK6sc6at5hedffc0nhdcab6ivuq @@ -221,3 +238,8 @@ create index idx_registry_lock_registrar_id on "RegistryLock" (registrar_id); add constraint FKo0gw90lpo1tuee56l0nb6y6g5 foreign key (revision_id) references "PremiumList"; + + alter table if exists "ReservedEntry" + add constraint FKgq03rk0bt1hb915dnyvd3vnfc + foreign key (revision_id) + references "ReservedList"; diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql index 5aafd926d..5a21eb19b 100644 --- a/db/src/main/resources/sql/schema/nomulus.golden.sql +++ b/db/src/main/resources/sql/schema/nomulus.golden.sql @@ -141,6 +141,49 @@ CREATE SEQUENCE public."RegistryLock_revision_id_seq" ALTER SEQUENCE public."RegistryLock_revision_id_seq" OWNED BY public."RegistryLock".revision_id; +-- +-- Name: ReservedEntry; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public."ReservedEntry" ( + revision_id bigint NOT NULL, + comment text, + reservation_type integer NOT NULL, + domain_label text NOT NULL +); + + +-- +-- Name: ReservedList; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public."ReservedList" ( + revision_id bigint NOT NULL, + creation_timestamp timestamp with time zone NOT NULL, + name text NOT NULL, + should_publish boolean NOT NULL +); + + +-- +-- Name: ReservedList_revision_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public."ReservedList_revision_id_seq" + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: ReservedList_revision_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public."ReservedList_revision_id_seq" OWNED BY public."ReservedList".revision_id; + + -- -- Name: ClaimsList revision_id; Type: DEFAULT; Schema: public; Owner: - -- @@ -162,6 +205,13 @@ ALTER TABLE ONLY public."PremiumList" ALTER COLUMN revision_id SET DEFAULT nextv ALTER TABLE ONLY public."RegistryLock" ALTER COLUMN revision_id SET DEFAULT nextval('public."RegistryLock_revision_id_seq"'::regclass); +-- +-- Name: ReservedList revision_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."ReservedList" ALTER COLUMN revision_id SET DEFAULT nextval('public."ReservedList_revision_id_seq"'::regclass); + + -- -- Name: ClaimsEntry ClaimsEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -202,6 +252,22 @@ ALTER TABLE ONLY public."RegistryLock" ADD CONSTRAINT "RegistryLock_pkey" PRIMARY KEY (revision_id); +-- +-- Name: ReservedEntry ReservedEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."ReservedEntry" + ADD CONSTRAINT "ReservedEntry_pkey" PRIMARY KEY (revision_id, domain_label); + + +-- +-- Name: ReservedList ReservedList_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."ReservedList" + ADD CONSTRAINT "ReservedList_pkey" PRIMARY KEY (revision_id); + + -- -- Name: RegistryLock idx_registry_lock_repo_id_revision_id; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -231,6 +297,13 @@ CREATE INDEX idx_registry_lock_verification_code ON public."RegistryLock" USING CREATE INDEX premiumlist_name_idx ON public."PremiumList" USING btree (name); +-- +-- Name: reservedlist_name_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX reservedlist_name_idx ON public."ReservedList" USING btree (name); + + -- -- Name: ClaimsEntry fk6sc6at5hedffc0nhdcab6ivuq; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -239,6 +312,14 @@ ALTER TABLE ONLY public."ClaimsEntry" ADD CONSTRAINT fk6sc6at5hedffc0nhdcab6ivuq FOREIGN KEY (revision_id) REFERENCES public."ClaimsList"(revision_id); +-- +-- Name: ReservedEntry fkgq03rk0bt1hb915dnyvd3vnfc; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."ReservedEntry" + ADD CONSTRAINT fkgq03rk0bt1hb915dnyvd3vnfc FOREIGN KEY (revision_id) REFERENCES public."ReservedList"(revision_id); + + -- -- Name: PremiumEntry fko0gw90lpo1tuee56l0nb6y6g5; Type: FK CONSTRAINT; Schema: public; Owner: - --