Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RegistryLock SQL schema #243

Merged
merged 20 commits into from Sep 10, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -0,0 +1,237 @@
// 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.domain;

import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.toJodaDateTime;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;

import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import java.time.ZonedDateTime;
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 javax.persistence.Index;
import javax.persistence.Table;
import org.joda.time.DateTime;

/**
* Represents a registry lock/unlock object, meaning that the domain is locked on the registry
* level.
*
* <p>Registry locks must be requested through the registrar console by a lock-enabled contact, then
* confirmed through email within a certain length of time. Until that confirmation is processed,
* the completion time will remain null and the lock will have no effect. The same applies for
* unlock actions.
*
* <p>Note that there will be at most one row per domain with a null copmleted time -- this means
* that there is at most one pending action per domain. This is enforced at the logic level.
*
* <p>Note as well that in the case of a retry of a write after an unexpected success, the unique
* constraint on {@link #verificationCode} means that the second write will fail.
*/
@Entity
@Table(
// Unique constraint to get around Hibernate's failure to handle
// auto-increment field in composite primary key.
indexes =
@Index(
name = "idx_registry_lock_repo_id_revision_id",
columnList = "repo_id, revision_id",
unique = true))
public final class RegistryLock extends ImmutableObject implements Buildable {

/** Describes the action taken by the user. */
public enum Action {
LOCK,
UNLOCK
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "revision_id", nullable = false)
private Long revisionId;

/** EPP repo ID of the domain in question. */
@Column(name = "repo_id", nullable = false)
private String repoId;

// TODO (b/140568328): remove this when everything is in Cloud SQL and we can join on "domain"
@Column(name = "domain_name", nullable = false)
private String domainName;

/**
* The ID of the registrar that performed the action -- this may be the admin ID if this action
* was performed by a superuser.
*/
@Column(name = "registrar_id", nullable = false)
private String registrarId;

/** The POC that performed the action, or null if it was a superuser. */
@Column(name = "registrar_poc_id")
private String registrarPocId;

/**
* Lock action is immutable and describes whether the action performed was a lock or an unlock.
*/
@Enumerated(EnumType.STRING)
@Column(name = "action", nullable = false)
private Action action;

/** Creation timestamp is when the lock/unlock is first requested. */
@Column(name = "creation_timestamp", nullable = false)
private ZonedDateTime creationTimestamp;

/**
* Completion timestamp is when the user has verified the lock/unlock, when this object de facto
* becomes immutable. If this field is null, it means that the lock has not been verified yet (and
* thus not been put into effect).
*/
@Column(name = "completion_timestamp")
private ZonedDateTime completionTimestamp;

/**
* The user must provide the random verification code in order to complete the lock and move the
* status from PENDING to COMPLETED.
*/
@Column(name = "verification_code", nullable = false)
private String verificationCode;

/**
* True iff this action was taken by a superuser, in response to something like a URS request. In
* this case, the action was performed by a registry admin rather than a registrar.
*/
@Column(name = "is_superuser", nullable = false)
private boolean isSuperuser;

public String getRepoId() {
return repoId;
}

public String getDomainName() {
return domainName;
}

public String getRegistrarId() {
return registrarId;
}

public String getRegistrarPocId() {
return registrarPocId;
}

public Action getAction() {
return action;
}

public DateTime getCreationTimestamp() {
return toJodaDateTime(creationTimestamp);
}

public DateTime getCompletionTimestamp() {
return toJodaDateTime(completionTimestamp);
}

public String getVerificationCode() {
return verificationCode;
}

public boolean isSuperuser() {
return isSuperuser;
}

public Long getRevisionId() {
return revisionId;
}

@Override
public Builder asBuilder() {
return new Builder(clone(this));
}

/** Builder for {@link google.registry.schema.domain.RegistryLock}. */
public static class Builder extends Buildable.Builder<RegistryLock> {
public Builder() {}

private Builder(RegistryLock instance) {
super(instance);
}

@Override
public RegistryLock build() {
checkArgumentNotNull(getInstance().repoId, "Repo ID cannot be null");
checkArgumentNotNull(getInstance().domainName, "Domain name cannot be null");
checkArgumentNotNull(getInstance().registrarId, "Registrar ID cannot be null");
checkArgumentNotNull(getInstance().action, "Action cannot be null");
checkArgumentNotNull(getInstance().creationTimestamp, "Creation timestamp cannot be null");
checkArgumentNotNull(getInstance().verificationCode, "Verification codecannot be null");
checkArgument(
getInstance().registrarPocId != null || getInstance().isSuperuser,
"Registrar POC ID must be provided if superuser is false");
return super.build();
}

public Builder setRepoId(String repoId) {
getInstance().repoId = repoId;
return this;
}

public Builder setDomainName(String domainName) {
getInstance().domainName = domainName;
return this;
}

public Builder setRegistrarId(String registrarId) {
getInstance().registrarId = registrarId;
return this;
}

public Builder setRegistrarPocId(String registrarPocId) {
getInstance().registrarPocId = registrarPocId;
return this;
}

public Builder setAction(Action action) {
getInstance().action = action;
return this;
}

public Builder setCreationTimestamp(DateTime creationTimestamp) {
getInstance().creationTimestamp = toZonedDateTime(creationTimestamp);
return this;
}

public Builder setCompletionTimestamp(DateTime lockTimestamp) {
getInstance().completionTimestamp = toZonedDateTime(lockTimestamp);
return this;
}

public Builder setVerificationCode(String verificationCode) {
getInstance().verificationCode = verificationCode;
return this;
}

public Builder isSuperuser(boolean isSuperuser) {
getInstance().isSuperuser = isSuperuser;
return this;
}
}
}
@@ -28,6 +28,7 @@
import google.registry.model.transfer.BaseTransferObject;
import google.registry.model.transfer.TransferData;
import google.registry.persistence.NomulusNamingStrategy;
import google.registry.schema.domain.RegistryLock;
import google.registry.schema.tld.PremiumList;
import google.registry.schema.tmch.ClaimsList;
import java.io.IOException;
@@ -68,6 +69,7 @@
GracePeriod.class,
Period.class,
PremiumList.class,
RegistryLock.class,
TransferData.class,
Trid.class);

@@ -20,6 +20,7 @@
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
-->
<class>google.registry.model.domain.DomainBase</class>
<class>google.registry.schema.domain.RegistryLock</class>
<class>google.registry.schema.tmch.ClaimsList</class>
<class>google.registry.model.transfer.BaseTransferObject</class>
<class>google.registry.schema.tld.PremiumList</class>
@@ -0,0 +1,30 @@
-- 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 "RegistryLock" (
revision_id BIGSERIAL NOT NULL,
action TEXT NOT NULL,
completion_timestamp TIMESTAMPTZ,
creation_timestamp TIMESTAMPTZ NOT NULL,
domain_name TEXT NOT NULL,
is_superuser BOOLEAN NOT NULL,
registrar_id TEXT NOT NULL,
registrar_poc_id TEXT,
repo_id TEXT NOT NULL,
verification_code TEXT NOT NULL,
PRIMARY KEY (revision_id)
);

ALTER TABLE IF EXISTS "RegistryLock"
ADD CONSTRAINT idx_registry_lock_repo_id_revision_id UNIQUE (repo_id, revision_id);
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.