mirror of
https://github.com/google/nomulus.git
synced 2025-07-24 11:38:35 +02:00
Add TypeAdapters for VKey objects (#2194)
GSON doesn't allow for clean (de)serialization of Class or Serializable objects which we'll need for converting VKeys to/from JSON.
This commit is contained in:
parent
9330e3a50d
commit
7332b1fa38
8 changed files with 195 additions and 11 deletions
|
@ -0,0 +1,40 @@
|
|||
// 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.model.adapters;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Adapter factory that allows for (de)serialization of Class objects in GSON.
|
||||
*
|
||||
* <p>GSON's built-in adapter for Class objects throws an exception, but there are situations where
|
||||
* we want to (de)serialize these, such as in VKeys. This instructs GSON to look for our custom
|
||||
* {@link ClassTypeAdapter} rather than the default.
|
||||
*/
|
||||
public class ClassProcessingTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if (Class.class.isAssignableFrom(typeToken.getRawType())) {
|
||||
// in this case, T is a class object
|
||||
return (TypeAdapter<T>) new ClassTypeAdapter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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.model.adapters;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* TypeAdapter for {@link Class} objects.
|
||||
*
|
||||
* <p>GSON's default adapter doesn't allow this, but we want to allow for (de)serialization of Class
|
||||
* objects for containers like VKeys using the full name of the class.
|
||||
*/
|
||||
public class ClassTypeAdapter extends TypeAdapter<Class<?>> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Class value) throws IOException {
|
||||
out.value(value.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> read(JsonReader reader) throws IOException {
|
||||
String stringValue = reader.nextString();
|
||||
if (stringValue.equals("null")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Class.forName(stringValue);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// this should not happen...
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// 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.model.adapters;
|
||||
|
||||
import google.registry.util.StringBaseTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* TypeAdapter for {@link Serializable} objects.
|
||||
*
|
||||
* <p>VKey keys (primary keys in SQL) are usually represented by either a long or a String. There
|
||||
* are a couple situations (CursorId, HistoryEntryId) where the Serializable in question is a
|
||||
* complex object, but we do not need to worry about (de)serializing those objects to/from JSON.
|
||||
*/
|
||||
public class SerializableJsonTypeAdapter extends StringBaseTypeAdapter<Serializable> {
|
||||
|
||||
@Override
|
||||
protected Serializable fromString(String stringValue) throws IOException {
|
||||
try {
|
||||
return Long.parseLong(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return stringValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -128,14 +128,14 @@ public class DomainBase extends EppResource
|
|||
String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
@Transient Set<VKey<Host>> nsHosts;
|
||||
@Expose @Transient Set<VKey<Host>> nsHosts;
|
||||
|
||||
/** Contacts. */
|
||||
VKey<Contact> adminContact;
|
||||
@Expose VKey<Contact> adminContact;
|
||||
|
||||
VKey<Contact> billingContact;
|
||||
VKey<Contact> techContact;
|
||||
VKey<Contact> registrantContact;
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
|
|
|
@ -57,7 +57,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
|
|||
// The primary key for the referenced entity.
|
||||
@Expose Serializable key;
|
||||
|
||||
Class<? extends T> kind;
|
||||
@Expose Class<? extends T> kind;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
VKey() {}
|
||||
|
|
|
@ -21,11 +21,14 @@ import com.google.gson.TypeAdapterFactory;
|
|||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import google.registry.model.adapters.ClassProcessingTypeAdapterFactory;
|
||||
import google.registry.model.adapters.CurrencyJsonAdapter;
|
||||
import google.registry.model.adapters.SerializableJsonTypeAdapter;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import google.registry.util.DateTimeTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
|
@ -69,9 +72,11 @@ public class GsonUtils {
|
|||
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.registerTypeAdapter(CurrencyUnit.class, new CurrencyJsonAdapter())
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(Serializable.class, new SerializableJsonTypeAdapter())
|
||||
.registerTypeAdapterFactory(new ClassProcessingTypeAdapterFactory())
|
||||
.registerTypeAdapterFactory(new GsonPostProcessableTypeAdapterFactory())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// 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.model.adapters;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link ClassTypeAdapter} and {@link SerializableJsonTypeAdapter}. */
|
||||
public class VKeyAdapterTest {
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
@Test
|
||||
void testVKeyConversion_string() {
|
||||
VKey<Domain> vkey = VKey.create(Domain.class, "someRepoId");
|
||||
String vkeyJson = GSON.toJson(vkey);
|
||||
assertThat(vkeyJson)
|
||||
.isEqualTo(
|
||||
"{\"key\":\"someRepoId\",\"kind\":" + "\"google.registry.model.domain.Domain\"}");
|
||||
assertThat(GSON.fromJson(vkeyJson, VKey.class)).isEqualTo(vkey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVKeyConversion_number() {
|
||||
VKey<BillingEvent> vkey = VKey.create(BillingEvent.class, 203L);
|
||||
String vkeyJson = GSON.toJson(vkey);
|
||||
assertThat(vkeyJson)
|
||||
.isEqualTo("{\"key\":203,\"kind\":" + "\"google.registry.model.billing.BillingEvent\"}");
|
||||
assertThat(GSON.fromJson(vkeyJson, VKey.class)).isEqualTo(vkey);
|
||||
}
|
||||
}
|
|
@ -66,10 +66,14 @@ public class ConsoleDomainGetActionTest {
|
|||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
assertThat(RESPONSE.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"domainName\":\"exists.tld\",\"registrationExpirationTime\":"
|
||||
+ "\"294247-01-10T04:00:54.775Z\",\"lastTransferTime\":\"null\",\"repoId\":"
|
||||
+ "\"2-TLD\",\"currentSponsorRegistrarId\":\"TheRegistrar\",\"creationRegistrarId\""
|
||||
+ ":\"TheRegistrar\",\"creationTime\":{\"creationTime\":"
|
||||
"{\"domainName\":\"exists.tld\",\"adminContact\":{\"key\":\"3-ROID\",\"kind\":"
|
||||
+ "\"google.registry.model.contact.Contact\"},\"techContact\":{\"key\":\"3-ROID\","
|
||||
+ "\"kind\":\"google.registry.model.contact.Contact\"},\"registrantContact\":"
|
||||
+ "{\"key\":\"3-ROID\",\"kind\":\"google.registry.model.contact.Contact\"},"
|
||||
+ "\"registrationExpirationTime\":\"294247-01-10T04:00:54.775Z\","
|
||||
+ "\"lastTransferTime\":\"null\",\"repoId\":\"2-TLD\","
|
||||
+ "\"currentSponsorRegistrarId\":\"TheRegistrar\",\"creationRegistrarId\":"
|
||||
+ "\"TheRegistrar\",\"creationTime\":{\"creationTime\":"
|
||||
+ "\"1970-01-01T00:00:00.000Z\"},\"lastEppUpdateTime\":\"null\",\"statuses\":"
|
||||
+ "[\"INACTIVE\"]}");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue