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 668114481..7b81397a8 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -625,8 +625,11 @@ public class DomainBase extends EppResource @Override public VKey createVKey() { - // TODO(mmuller): create symmetric keys if we can ever reload both sides. - return VKey.createOfy(DomainBase.class, Key.create(this)); + return VKey.create(DomainBase.class, getRepoId(), Key.create(this)); + } + + public static VKey createVKey(Key key) { + return VKey.create(DomainBase.class, key.getName(), key); } /** Predicate to determine if a given {@link DesignatedContact} is the registrant. */ diff --git a/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java b/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java index c7bf520b5..9a3e11873 100644 --- a/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java +++ b/core/src/main/java/google/registry/model/translators/VKeyTranslatorFactory.java @@ -15,6 +15,7 @@ package google.registry.model.translators; import static com.google.common.base.Functions.identity; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static google.registry.model.EntityClasses.ALL_CLASSES; @@ -22,6 +23,8 @@ import com.google.appengine.api.datastore.Key; import com.google.common.collect.ImmutableMap; import com.googlecode.objectify.annotation.EntitySubclass; import google.registry.persistence.VKey; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; /** * Translator factory for VKey. @@ -45,17 +48,46 @@ public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory createVKey(Key datastoreKey) { + return createVKey(com.googlecode.objectify.Key.create(datastoreKey)); + } + + /** Create a VKey from an objectify Key. */ + public static VKey createVKey(com.googlecode.objectify.Key key) { + if (key == null) { + return null; + } + + // Try to create the VKey from its reference type. + Class clazz = CLASS_REGISTRY.get(key.getKind()); + checkArgument(clazz != null, "Unknown Key type: %s", key.getKind()); + try { + Method createVKeyMethod = + clazz.getDeclaredMethod("createVKey", com.googlecode.objectify.Key.class); + return (VKey) createVKeyMethod.invoke(null, new Object[] {key}); + } catch (NoSuchMethodException e) { + // Revert to an ofy vkey for now. TODO(mmuller): remove this when all classes with VKeys have + // converters. + return VKey.createOfy(clazz, key); + } catch (IllegalAccessException | InvocationTargetException e) { + // If we have a createVKey(Key) method with incorrect permissions or that is non-static, this + // is probably an error so let's reported. + throw new RuntimeException(e); + } + } + + /** Create a VKey from a URL-safe string representation. */ + public static VKey createVKey(String urlSafe) { + return createVKey(com.googlecode.objectify.Key.create(urlSafe)); + } + @Override public SimpleTranslator createTranslator() { return new SimpleTranslator() { @Override public VKey loadValue(Key datastoreValue) { - // TODO(mmuller): we need to call a method on refClass to also reconstitute the SQL key. - return datastoreValue == null - ? null - : VKey.createOfy( - CLASS_REGISTRY.get(datastoreValue.getKind()), - com.googlecode.objectify.Key.create(datastoreValue)); + return createVKey(datastoreValue); } @Override diff --git a/core/src/test/java/google/registry/model/translators/VKeyTranslatorFactoryTest.java b/core/src/test/java/google/registry/model/translators/VKeyTranslatorFactoryTest.java new file mode 100644 index 000000000..7d08920de --- /dev/null +++ b/core/src/test/java/google/registry/model/translators/VKeyTranslatorFactoryTest.java @@ -0,0 +1,71 @@ +// Copyright 2020 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.translators; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.testing.DatastoreHelper.newDomainBase; +import static google.registry.testing.DatastoreHelper.persistActiveContact; + +import com.googlecode.objectify.Key; +import google.registry.model.domain.DomainBase; +import google.registry.model.ofy.CommitLogBucket; +import google.registry.persistence.VKey; +import google.registry.testing.AppEngineRule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class VKeyTranslatorFactoryTest { + + @RegisterExtension public final AppEngineRule appEngine = + AppEngineRule.builder() + .withDatastore() + .build(); + + public VKeyTranslatorFactoryTest() {} + + @Test + void testEntityWithVKeyCreate() { + // Creating an objectify key instead of a datastore key as this should get a correctly formatted + // key path. + DomainBase domain = newDomainBase("example.com", "ROID-1", persistActiveContact("contact-1")); + Key key = Key.create(domain); + VKey vkey = VKeyTranslatorFactory.createVKey(key); + assertThat(vkey.getKind()).isEqualTo(DomainBase.class); + assertThat(vkey.getOfyKey()).isEqualTo(key); + assertThat(vkey.getSqlKey()).isEqualTo("ROID-1"); + } + + @Test + void testEntityWithoutVKeyCreate() { + CommitLogBucket bucket = new CommitLogBucket.Builder().build(); + Key key = Key.create(bucket); + VKey vkey = VKeyTranslatorFactory.createVKey(key); + assertThat(vkey.getKind()).isEqualTo(CommitLogBucket.class); + assertThat(vkey.getOfyKey()).isEqualTo(key); + assertThat(vkey.maybeGetSqlKey().isPresent()).isFalse(); + } + + @Test + void testUrlSafeKey() { + // Creating an objectify key instead of a datastore key as this should get a correctly formatted + // key path. + DomainBase domain = newDomainBase("example.com", "ROID-1", persistActiveContact("contact-1")); + Key key = Key.create(domain); + VKey vkey = (VKey) VKeyTranslatorFactory.createVKey(key.getString()); + assertThat(vkey.getKind()).isEqualTo(DomainBase.class); + assertThat(vkey.getOfyKey()).isEqualTo(key); + assertThat(vkey.getSqlKey()).isEqualTo("ROID-1"); + } +}