Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Fix inconsistent enum constant names between schema and serialization #181

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.google.api.client.util.Maps;
import com.google.api.server.spi.TypeLoader;
import com.google.api.server.spi.config.Description;
import com.google.api.server.spi.config.ResourcePropertySchema;
import com.google.api.server.spi.config.ResourceSchema;
import com.google.api.server.spi.config.annotationreader.ApiAnnotationIntrospector;
Expand All @@ -18,7 +17,6 @@
import com.google.common.reflect.TypeToken;

import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

Expand Down Expand Up @@ -172,15 +170,13 @@ private Schema getOrCreateTypeForConfig(
schemaByApiKeys.put(key, schema);
return schema;
} else if (Types.isEnumType(type)) {
Map<String, String> valuesAndDescriptions = Types.getEnumValuesAndDescriptions(type);
Schema.Builder builder = Schema.builder()
.setName(Types.getSimpleName(type, config.getSerializationConfig()))
.setType("string");
for (java.lang.reflect.Field field : type.getRawType().getFields()) {
if (field.isEnumConstant()) {
builder.addEnumValue(field.getName());
Description description = field.getAnnotation(Description.class);
builder.addEnumDescription(description == null ? "" : description.value());
}
for (Entry<String, String> entry : valuesAndDescriptions.entrySet()) {
builder.addEnumValue(entry.getKey());
builder.addEnumDescription(entry.getValue());
}
schema = builder.build();
typesForConfig.put(type, schema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
*/
package com.google.api.server.spi.config.model;

import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.util.EnumValues;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.Preconditions;
import com.google.api.server.spi.ObjectMapperUtil;
import com.google.api.server.spi.config.Description;
import com.google.api.server.spi.config.ResourceSchema;
import com.google.api.server.spi.config.ResourceTransformer;
import com.google.api.server.spi.config.Transformer;
Expand All @@ -32,6 +36,8 @@
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.Nullable;
Expand All @@ -56,6 +62,33 @@ public static boolean isEnumType(TypeToken<?> type) {
return type.isSubtypeOf(Enum.class);
}

/**
* Determines enum constant names for serialization, and their description for API schema.
*
* @param type an enum type
* @return a map containing the enum field names as keys,
* and schema description as values (possibly empty but not null).
*/
public static Map<String, String> getEnumValuesAndDescriptions(TypeToken<Enum<?>> type) {
Class<Enum<?>> enumType = (Class<Enum<?>>) type.getRawType();
Map<String, String> descriptions = new HashMap<>();
for (java.lang.reflect.Field field : enumType.getFields()) {
if (field.isEnumConstant()) {
Description description = field.getAnnotation(Description.class);
descriptions.put(field.getName(), description != null ? description.value() : "");
}
}
SerializationConfig serializationConfig = ObjectMapperUtil.createStandardObjectMapper()
.getSerializationConfig();
EnumValues enumValues = EnumValues.construct(serializationConfig, enumType);
Map<String, String> valueAndDescription = new LinkedHashMap<>();
for (Enum<?> enumConstant : enumType.getEnumConstants()) {
valueAndDescription.put(enumValues.serializedValueFor(enumConstant).toString(),
descriptions.get(enumConstant.name()));
}
return valueAndDescription;
}

/**
* Returns whether or not this type is a {@link Map}. This excludes {@link GenericData}, which is
* used by the Google Java client library as a supertype of resource types with concrete fields.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.google.api.server.spi.ObjectMapperUtil;
import com.google.api.server.spi.Strings;
import com.google.api.server.spi.TypeLoader;
import com.google.api.server.spi.config.Description;
import com.google.api.server.spi.config.annotationreader.ApiAnnotationIntrospector;
import com.google.api.server.spi.config.model.ApiConfig;
import com.google.api.server.spi.config.model.ApiKey;
Expand All @@ -34,6 +33,7 @@
import com.google.api.server.spi.config.model.SchemaRepository;
import com.google.api.server.spi.config.model.AuthScopeRepository;
import com.google.api.server.spi.config.model.StandardParameters;
import com.google.api.server.spi.config.model.Types;
import com.google.api.server.spi.config.scope.AuthScopeExpression;
import com.google.api.server.spi.config.scope.AuthScopeExpressions;
import com.google.api.services.discovery.model.DirectoryList;
Expand Down Expand Up @@ -363,14 +363,13 @@ private JsonSchema convertMethodParameter(
}

if (parameterConfig.isEnum()) {
Map<String, String> enumValuesAndDescriptions = Types
.getEnumValuesAndDescriptions((TypeToken<Enum<?>>) type);
List<String> enumValues = Lists.newArrayList();
List<String> enumDescriptions = Lists.newArrayList();
for (java.lang.reflect.Field field : type.getRawType().getFields()) {
if (field.isEnumConstant()) {
enumValues.add(field.getName());
Description description = field.getAnnotation(Description.class);
enumDescriptions.add(description == null ? "" : description.value());
}
for (Entry<String, String> entry : enumValuesAndDescriptions.entrySet()) {
enumValues.add(entry.getKey());
enumDescriptions.add(entry.getValue());
}
schema.setEnum(enumValues);
schema.setEnumDescriptions(enumDescriptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.api.server.spi.config.model.Schema;
import com.google.api.server.spi.config.model.Schema.Field;
import com.google.api.server.spi.config.model.SchemaRepository;
import com.google.api.server.spi.config.model.Types;
import com.google.api.server.spi.config.validation.ApiConfigValidator;
import com.google.api.server.spi.types.DateAndTime;
import com.google.api.server.spi.types.SimpleDate;
Expand Down Expand Up @@ -487,11 +488,7 @@ private Path getOrCreatePath(Swagger swagger, ApiMethodConfig methodConfig) {
}

private static List<String> getEnumValues(TypeToken<?> t) {
List<String> values = Lists.newArrayList();
for (Object value : t.getRawType().getEnumConstants()) {
values.add(value.toString());
}
return values;
return new ArrayList<>(Types.getEnumValuesAndDescriptions((TypeToken<Enum<?>>) t).keySet());
}

private static SecuritySchemeDefinition toScheme(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,30 @@ public void getOrAdd_enum() throws Exception {
.setName("TestEnum")
.setType("string")
.addEnumValue("VALUE1")
.addEnumValue("VALUE2")
.addEnumValue("value_2")
.addEnumDescription("")
.addEnumDescription("")
.build());
}

@Test
public void getOrAdd_enum_disableJacksonAnnotations() throws Exception {
System.setProperty(EndpointsFlag.JSON_USE_JACKSON_ANNOTATIONS.systemPropertyName, "false");
try {
assertThat(repo.getOrAdd(TypeToken.of(TestEnum.class), config))
.isEqualTo(Schema.builder()
.setName("TestEnum")
.setType("string")
.addEnumValue("VALUE1")
.addEnumValue("VALUE2")
.addEnumDescription("")
.addEnumDescription("")
.build());
} finally {
System.clearProperty(EndpointsFlag.JSON_USE_JACKSON_ANNOTATIONS.systemPropertyName);
}
}

@Test
public void getOrAdd_recursiveSchema() throws Exception {
TypeToken<SelfReferencingObject> type = TypeToken.of(SelfReferencingObject.class);
Expand Down Expand Up @@ -270,7 +288,7 @@ public void getOrAdd_multipleApis() throws Exception {
.setName("TestEnum")
.setType("string")
.addEnumValue("VALUE1")
.addEnumValue("VALUE2")
.addEnumValue("value_2")
.addEnumDescription("")
.addEnumDescription("")
.build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"value": {
"enum": [
"VALUE1",
"VALUE2"
"value_2"
],
"enumDescriptions": [
"",
Expand Down Expand Up @@ -110,7 +110,7 @@
},
"TestEnum": {
"id": "TestEnum",
"enum" : [ "VALUE1", "VALUE2" ],
"enum" : [ "VALUE1", "value_2" ],
"enumDescriptions" : [ "", "" ],
"type": "string"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"type": "string",
"enum": [
"VALUE1",
"VALUE2"
"value_2"
]
}
],
Expand All @@ -50,7 +50,7 @@
"type": "string",
"enum": [
"VALUE1",
"VALUE2"
"value_2"
]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package com.google.api.server.spi.testing;

import com.fasterxml.jackson.annotation.JsonProperty;

public enum TestEnum {
VALUE1, VALUE2
VALUE1,
@JsonProperty("value_2")
VALUE2;
}