From 2cc8fbec6e33488a32feea4f66f20bff90af9d32 Mon Sep 17 00:00:00 2001 From: Michael Muller Date: Fri, 16 Aug 2019 12:47:17 -0400 Subject: [PATCH] Add a generate_schema command Add a generate_schema command to nomulus tool and add the necessary instrumentation to EppResource and DomainBase to allow us to generate a proof-of-concept schema for DomainBase. --- .../dependency-license/allowed_licenses.json | 12 +++ .../google/registry/model/EppResource.java | 11 +-- .../registry/model/domain/DomainBase.java | 21 ++--- .../registry/tools/GenerateSchemaCommand.java | 78 +++++++++++++++++++ .../google/registry/tools/RegistryTool.java | 1 + .../tools/GenerateSchemaCommandTest.java | 62 +++++++++++++++ 6 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/google/registry/tools/GenerateSchemaCommand.java create mode 100644 core/src/test/java/google/registry/tools/GenerateSchemaCommandTest.java diff --git a/config/dependency-license/allowed_licenses.json b/config/dependency-license/allowed_licenses.json index 473ddc8d7..b88c7d093 100644 --- a/config/dependency-license/allowed_licenses.json +++ b/config/dependency-license/allowed_licenses.json @@ -75,6 +75,9 @@ { "moduleLicense": "BSD style" }, + { + "moduleLicense": "BSD-2-Clause" + }, { "moduleLicense": "New BSD License" }, @@ -84,6 +87,9 @@ { "moduleLicense": "The BSD License" }, + { + "moduleLicense": "The PostgreSQL License" + }, { "moduleLicense": "CC0 1.0 Universal License" }, @@ -105,6 +111,9 @@ { "moduleLicense": "\\n Dual license consisting of the CDDL v1.1 and GPL v2\\n " }, + { + "moduleLicense": "Eclipse Distribution License v. 1.0" + }, { "moduleLicense": "Eclipse Public License - Version 1.0" }, @@ -135,6 +144,9 @@ { "moduleLicense": "GNU Lesser Public License" }, + { + "moduleLicense": "GNU Library General Public License v2.1 or later" + }, { "moduleLicense": "The Go license" }, diff --git a/core/src/main/java/google/registry/model/EppResource.java b/core/src/main/java/google/registry/model/EppResource.java index 22dbca018..95397250a 100644 --- a/core/src/main/java/google/registry/model/EppResource.java +++ b/core/src/main/java/google/registry/model/EppResource.java @@ -50,10 +50,13 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; +import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; import org.joda.time.DateTime; import org.joda.time.Duration; /** An EPP entity object (i.e. a domain, contact, or host). */ +@MappedSuperclass public abstract class EppResource extends BackupGroupRoot implements Buildable { /** @@ -62,8 +65,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable { *

This is in the (\w|_){1,80}-\w{1,8} format specified by RFC 5730 for roidType. * @see RFC 5730 */ - @Id - String repoId; + @Id @javax.persistence.Id String repoId; /** The ID of the registrar that is currently sponsoring this resource. */ @Index @@ -85,8 +87,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable { // Map the method to XML, not the field, because if we map the field (with an adaptor class) it // will never be omitted from the xml even if the timestamp inside creationTime is null and we // return null from the adaptor. (Instead it gets written as an empty tag.) - @Index - CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); + @Index @Transient CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); /** * The time when this resource was or will be deleted. @@ -115,7 +116,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable { DateTime lastEppUpdateTime; /** Status values associated with this resource. */ - Set status; + @Transient Set status; /** * Sorted map of {@link DateTime} keys (modified time) to {@link CommitLogManifest} entries. diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java index 527ba4691..cee64c70c 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -69,6 +69,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import javax.annotation.Nullable; +import javax.persistence.Transient; import org.joda.time.DateTime; import org.joda.time.Interval; @@ -83,6 +84,8 @@ import org.joda.time.Interval; */ @ReportedOn @Entity +@javax.persistence.Entity +@javax.persistence.Table(name = "domain") @ExternalMessagingName("domain") public class DomainBase extends EppResource implements ForeignKeyedEppResource, ResourceWithTransferData { @@ -115,18 +118,17 @@ public class DomainBase extends EppResource String tld; /** References to hosts that are the nameservers for the domain. */ - @Index - Set> nsHosts; + @Index @Transient Set> nsHosts; /** * The union of the contacts visible via {@link #getContacts} and {@link #getRegistrant}. * *

These are stored in one field so that we can query across all contacts at once. */ - Set allContacts; + @Transient Set allContacts; /** Authorization info (aka transfer secret) of the domain. */ - DomainAuthInfo authInfo; + @Transient DomainAuthInfo authInfo; /** * Data used to construct DS records for this domain. @@ -134,14 +136,13 @@ public class DomainBase extends EppResource *

This is {@literal @}XmlTransient because it needs to be returned under the "extension" tag * of an info response rather than inside the "infData" tag. */ - Set dsData; + @Transient Set dsData; /** * The claims notice supplied when this application or domain was created, if there was one. It's * {@literal @}XmlTransient because it's not returned in an info response. */ - @IgnoreSave(IfNull.class) - LaunchNotice launchNotice; + @IgnoreSave(IfNull.class) @Transient LaunchNotice launchNotice; /** * Name of first IDN table associated with TLD that matched the characters in this domain label. @@ -152,7 +153,7 @@ public class DomainBase extends EppResource String idnTableName; /** Fully qualified host names of this domain's active subordinate hosts. */ - Set subordinateHosts; + @Transient Set subordinateHosts; /** When this domain's registration will expire. */ DateTime registrationExpirationTime; @@ -187,7 +188,7 @@ public class DomainBase extends EppResource Key autorenewPollMessage; /** The unexpired grace periods for this domain (some of which may not be active yet). */ - Set gracePeriods; + @Transient Set gracePeriods; /** * The id of the signed mark that was used to create this domain in sunrise. @@ -198,7 +199,7 @@ public class DomainBase extends EppResource String smdId; /** Data about any pending or past transfers on this domain. */ - TransferData transferData; + @Transient TransferData transferData; /** * The time that this resource was last transferred. diff --git a/core/src/main/java/google/registry/tools/GenerateSchemaCommand.java b/core/src/main/java/google/registry/tools/GenerateSchemaCommand.java new file mode 100644 index 000000000..4fde825a9 --- /dev/null +++ b/core/src/main/java/google/registry/tools/GenerateSchemaCommand.java @@ -0,0 +1,78 @@ +// 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.tools; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import google.registry.model.domain.DomainBase; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.schema.TargetType; + +/** + * Generates a schema for JPA annotated classes using hibernate. + * + *

Note that this isn't complete yet, as all of the persistent classes have not yet been + * converted. After converting a class, a call to "addAnnotatedClass()" for the new class must be + * added to the code below. + */ +@Parameters(separators = " =", commandDescription = "") +public class GenerateSchemaCommand implements Command { + + public static final int POSTGRESQL_PORT = 5432; + + @Parameter( + names = {"-o", "--out-file"}, + description = "") + String outFile; + + @Parameter( + names = {"-a", "--db-host"}, + description = "Database host name.") + String databaseHost; + + @Parameter( + names = {"-p", "--db-port"}, + description = "Database port number. This defaults to the postgresql default port.") + int databasePort = POSTGRESQL_PORT; + + @Override + public void run() { + Map settings = new HashMap<>(); + settings.put("connection.driver_class", "com.mysql.jdbc.Driver"); + settings.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect"); + settings.put( + "hibernate.connection.url", + "jdbc:postgresql://" + databaseHost + ":" + databasePort + "/postgres?useSSL=false"); + settings.put("hibernate.connection.username", "postgres"); + settings.put("hibernate.connection.password", "domain-registry"); + settings.put("hibernate.hbm2ddl.auto", "create"); + settings.put("show_sql", "true"); + + MetadataSources metadata = + new MetadataSources(new StandardServiceRegistryBuilder().applySettings(settings).build()); + metadata.addAnnotatedClass(DomainBase.class); + SchemaExport schemaExport = new SchemaExport(); + schemaExport.setHaltOnError(true); + schemaExport.setFormat(true); + schemaExport.setDelimiter(";"); + schemaExport.setOutputFile(outFile); + schemaExport.createOnly(EnumSet.of(TargetType.SCRIPT), metadata.buildMetadata()); + } +} diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java index c0d2f8c96..b603afdf4 100644 --- a/core/src/main/java/google/registry/tools/RegistryTool.java +++ b/core/src/main/java/google/registry/tools/RegistryTool.java @@ -61,6 +61,7 @@ public final class RegistryTool { .put("generate_dns_report", GenerateDnsReportCommand.class) .put("generate_escrow_deposit", GenerateEscrowDepositCommand.class) .put("generate_lordn", GenerateLordnCommand.class) + .put("generate_schema", GenerateSchemaCommand.class) .put("generate_zone_files", GenerateZoneFilesCommand.class) .put("get_allocation_token", GetAllocationTokenCommand.class) .put("get_claims_list", GetClaimsListCommand.class) diff --git a/core/src/test/java/google/registry/tools/GenerateSchemaCommandTest.java b/core/src/test/java/google/registry/tools/GenerateSchemaCommandTest.java new file mode 100644 index 000000000..4b9ef5430 --- /dev/null +++ b/core/src/test/java/google/registry/tools/GenerateSchemaCommandTest.java @@ -0,0 +1,62 @@ +// 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.tools; + +// import static org.mockito.Mockito.mock; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.File; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.testcontainers.containers.GenericContainer; + +@RunWith(JUnit4.class) +public class GenerateSchemaCommandTest extends CommandTestCase { + + private String containerHostName; + private int containerPort; + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Rule + public GenericContainer postgres = + new GenericContainer("postgres:9.6.12") + .withExposedPorts(GenerateSchemaCommand.POSTGRESQL_PORT); + + public GenerateSchemaCommandTest() {} + + @Before + public void setUp() { + containerHostName = postgres.getContainerIpAddress(); + containerPort = postgres.getMappedPort(GenerateSchemaCommand.POSTGRESQL_PORT); + } + + @Test + public void testSchemaGeneration() throws Exception { + runCommand( + "--out-file=" + tmp.getRoot() + File.separatorChar + "schema.sql", + "--db-host=" + containerHostName, + "--db-port=" + containerPort); + + // We're just interested in verifying that there is a schema file generated, we don't do any + // checks on the contents, this would make the test too brittle and serves no real purpose. + // TODO: try running the schema against the test database. + assertThat(new File(tmp.getRoot(), "schema.sql").exists()).isTrue(); + } +}