diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/Description.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/Description.java index e686e873..0f8040b2 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/Description.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/Description.java @@ -23,7 +23,7 @@ /** * Annotation to specify the description of an API parameter. */ -@Target(ElementType.PARAMETER) +@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Description { /** diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/Schema.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/Schema.java index 1fbeca12..66e55f25 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/Schema.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/Schema.java @@ -16,6 +16,7 @@ public abstract class Schema { /** The name of the schema. */ public abstract String name(); public abstract String type(); + @Nullable public abstract String description(); /** A map from field names to fields for the schema. */ public abstract ImmutableSortedMap fields(); @@ -44,6 +45,7 @@ public abstract static class Builder { public abstract Builder setName(String name); public abstract Builder setType(String type); + @Nullable public abstract Builder setDescription(String description); public abstract Builder setFields(ImmutableSortedMap fields); public Builder addField(String name, Field field) { fieldsBuilder.put(name, field); @@ -76,6 +78,8 @@ public static abstract class Field { /** The type classification of the field. */ public abstract FieldType type(); + @Nullable public abstract String description(); + /** * If {@link #type()} is {@link FieldType#OBJECT}, a reference to the schema type that the field * refers to. @@ -97,6 +101,7 @@ public static Builder builder() { public abstract static class Builder { public abstract Builder setName(String name); public abstract Builder setType(FieldType type); + @Nullable public abstract Builder setDescription(String description); public abstract Builder setSchemaReference(SchemaReference ref); public abstract Builder setArrayItemSchema(Field schema); public abstract Field build(); diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/SchemaRepository.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/SchemaRepository.java index 5b4c1d5b..69fa0910 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/SchemaRepository.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/SchemaRepository.java @@ -2,6 +2,7 @@ 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; @@ -154,8 +155,13 @@ private Schema getOrCreateTypeForConfig( .setName(Types.getSimpleName(type, config.getSerializationConfig())) .setType("string"); for (Object enumConstant : type.getRawType().getEnumConstants()) { - builder.addEnumValue(enumConstant.toString()); - builder.addEnumDescription(""); + builder.addEnumValue(((Enum) enumConstant).name()); + try { + final Description description = enumConstant.getClass().getField(((Enum) enumConstant).name()).getAnnotation(Description.class); + builder.addEnumDescription(description == null ? "" : description.value()); + } catch (NoSuchFieldException ex) { + builder.addEnumDescription(""); + } } schema = builder.build(); typesForConfig.put(type, schema); @@ -195,6 +201,14 @@ private Schema createBeanSchema( TypeToken propertyType = entry.getValue().getType(); if (propertyType != null) { Field.Builder fieldBuilder = Field.builder().setName(propertyName); + try { + Description propertyDescription = type.getRawType().getField(propertyName).getAnnotation(Description.class); + if (propertyDescription != null) { + fieldBuilder.setDescription(propertyDescription.value()); + } + } catch (NoSuchFieldException e) { + // ignore + } fillInFieldInformation(fieldBuilder, propertyType, typesForConfig, config); builder.addField(propertyName, fieldBuilder.build()); } diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/discovery/DiscoveryGenerator.java b/endpoints-framework/src/main/java/com/google/api/server/spi/discovery/DiscoveryGenerator.java index 234d7da0..52f44418 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/discovery/DiscoveryGenerator.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/discovery/DiscoveryGenerator.java @@ -20,6 +20,7 @@ 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; @@ -151,6 +152,9 @@ private RestDescription writeApi(ApiKey apiKey, Iterable apiConfigs, if (config.getDescription() != null) { doc.setDescription(config.getDescription()); } + if (config.getDocumentationLink() != null) { + doc.setDocumentationLink(config.getDocumentationLink()); + } if (config.getTitle() != null) { doc.setTitle(config.getTitle()); } @@ -227,6 +231,9 @@ private JsonSchema convertToDiscoverySchema(Schema schema) { } docSchema.setProperties(fields); } + if (schema.description() != null) { + docSchema.setDescription(schema.description()); + } if (!schema.enumValues().isEmpty()) { docSchema.setEnum(new ArrayList<>(schema.enumValues())); docSchema.setEnumDescriptions(new ArrayList<>(schema.enumDescriptions())); @@ -236,10 +243,13 @@ private JsonSchema convertToDiscoverySchema(Schema schema) { private JsonSchema convertToDiscoverySchema(Field f) { if (f.schemaReference() != null) { - return new JsonSchema().set$ref(f.schemaReference().get().name()); + return new JsonSchema() + .setDescription(f.description()) + .set$ref(f.schemaReference().get().name()); } JsonSchema fieldSchema = new JsonSchema() .setType(f.type().getDiscoveryType()) + .setDescription(f.description()) .setFormat(f.type().getDiscoveryFormat()); if (f.type() == FieldType.ARRAY) { fieldSchema.setItems(convertToDiscoverySchema(f.arrayItemSchema())); @@ -325,7 +335,12 @@ private JsonSchema convertMethodParameter( List enumDescriptions = Lists.newArrayList(); for (Object enumConstant : type.getRawType().getEnumConstants()) { enumValues.add(enumConstant.toString()); - enumDescriptions.add(""); // not current supported in annotations + try { + final Description description = enumConstant.getClass().getField(((Enum) enumConstant).name()).getAnnotation(Description.class); + enumDescriptions.add(description == null ? "" : description.value()); + } catch (NoSuchFieldException ex) { + enumDescriptions.add(""); + } } schema.setEnum(enumValues); schema.setEnumDescriptions(enumDescriptions); @@ -386,6 +401,7 @@ private DirectoryList generateDirectory(Map discoveryDo .setDescription(doc.getDescription()) .setDiscoveryLink("." + relativePath) .setDiscoveryRestUrl(context.getApiRoot() + "/discovery/v1" + relativePath) + .setDocumentationLink(doc.getDocumentationLink()) .setIcons(new Icons() .setX16("http://www.google.com/images/icons/product/search-16.gif") .setX32("http://www.google.com/images/icons/product/search-32.gif"))