diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/ApiResourceProperty.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/ApiResourceProperty.java index 7f1c30cd..da4b723c 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/ApiResourceProperty.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/ApiResourceProperty.java @@ -29,6 +29,12 @@ */ String name() default ""; + /** + * The description that the property represented by the annotated getter, setter, or field should appear + * as in the API. + */ + String description() default ""; + /** * Whether or not the property represented by the annotated getter, setter or field should be * ignored for the API. diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/ResourcePropertySchema.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/ResourcePropertySchema.java index e6c07c17..f7a3460d 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/ResourcePropertySchema.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/ResourcePropertySchema.java @@ -29,6 +29,7 @@ */ public class ResourcePropertySchema { private final TypeToken type; + private String description; private ResourcePropertySchema(TypeToken type) { this.type = type; @@ -48,6 +49,14 @@ public TypeToken getType() { return type; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + /** * Returns a default resource property schema for a given type. * diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationIntrospector.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationIntrospector.java index 4df27671..ea06c968 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationIntrospector.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationIntrospector.java @@ -83,11 +83,19 @@ public PropertyName findNameForSerialization(Annotated a) { @Override public PropertyName findNameForDeserialization(Annotated a) { - ApiResourceProperty apiName = a.getAnnotation(ApiResourceProperty.class); - if (apiName != null && apiName.ignored() != AnnotationBoolean.TRUE) { - return PropertyName.construct(apiName.name()); - } - return null; + ApiResourceProperty annotation = findAnnotation(a); + return annotation != null ? PropertyName.construct(annotation.name()) : null; + } + + @Override + public String findPropertyDescription(Annotated a) { + ApiResourceProperty annotation = findAnnotation(a); + return annotation != null ? annotation.description() : null; + } + + private ApiResourceProperty findAnnotation(Annotated a) { + ApiResourceProperty annotation = a.getAnnotation(ApiResourceProperty.class); + return annotation != null && annotation.ignored() != AnnotationBoolean.TRUE ? annotation : null; } @Override diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JacksonResourceSchemaProvider.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JacksonResourceSchemaProvider.java index 0efb72c2..18bbdb7e 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JacksonResourceSchemaProvider.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JacksonResourceSchemaProvider.java @@ -71,7 +71,9 @@ public ResourceSchema getResourceSchema(TypeToken type, ApiConfig config) { continue; } if (propertyType != null) { - schemaBuilder.addProperty(name, ResourcePropertySchema.of(propertyType)); + ResourcePropertySchema propertySchema = ResourcePropertySchema.of(propertyType); + propertySchema.setDescription(definition.getMetadata().getDescription()); + schemaBuilder.addProperty(name, propertySchema); } else { logger.warning("No type found for property '" + name + "' on class '" + type + "'."); } 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..4ba9b2fc 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 @@ -128,7 +128,7 @@ private Schema getOrCreateTypeForConfig( throw new IllegalArgumentException("Can't add a primitive type as a resource"); } else if (arrayItemType != null) { Field.Builder arrayItemSchema = Field.builder().setName(ARRAY_UNUSED_MSG); - fillInFieldInformation(arrayItemSchema, arrayItemType, typesForConfig, config); + fillInFieldInformation(arrayItemSchema, arrayItemType, null, typesForConfig, config); schema = Schema.builder() .setName(Types.getSimpleName(type, config.getSerializationConfig())) .setType("object") @@ -192,10 +192,11 @@ private Schema createBeanSchema( ResourceSchema schema = resourceSchemaProvider.getResourceSchema(type, config); for (Entry entry : schema.getProperties().entrySet()) { String propertyName = entry.getKey(); - TypeToken propertyType = entry.getValue().getType(); + ResourcePropertySchema propertySchema = entry.getValue(); + TypeToken propertyType = propertySchema.getType(); if (propertyType != null) { Field.Builder fieldBuilder = Field.builder().setName(propertyName); - fillInFieldInformation(fieldBuilder, propertyType, typesForConfig, config); + fillInFieldInformation(fieldBuilder, propertyType, propertySchema.getDescription(), typesForConfig, config); builder.addField(propertyName, fieldBuilder.build()); } } @@ -203,9 +204,10 @@ private Schema createBeanSchema( } private void fillInFieldInformation(Field.Builder builder, TypeToken fieldType, - Map, Schema> typesForConfig, ApiConfig config) { + String description, Map, Schema> typesForConfig, ApiConfig config) { FieldType ft = FieldType.fromType(fieldType); builder.setType(ft); + builder.setDescription(description); if (ft == FieldType.OBJECT || ft == FieldType.ENUM) { getOrCreateTypeForConfig(fieldType, typesForConfig, config); builder.setSchemaReference(SchemaReference.create(this, config, fieldType)); @@ -214,6 +216,7 @@ private void fillInFieldInformation(Field.Builder builder, TypeToken fieldTyp fillInFieldInformation( arrayItemBuilder, ApiAnnotationIntrospector.getSchemaType(Types.getArrayItemType(fieldType), config), + null, typesForConfig, config); builder.setArrayItemSchema(arrayItemBuilder.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 d98b083c..10503687 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 @@ -225,6 +225,7 @@ private JsonSchema convertToDiscoverySchema(Schema schema) { } docSchema.setProperties(fields); } + docSchema.setDescription(schema.description()); if (!schema.enumValues().isEmpty()) { docSchema.setEnum(new ArrayList<>(schema.enumValues())); docSchema.setEnumDescriptions(new ArrayList<>(schema.enumDescriptions())); @@ -238,6 +239,7 @@ private JsonSchema convertToDiscoverySchema(Field f) { } 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())); diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/swagger/SwaggerGenerator.java b/endpoints-framework/src/main/java/com/google/api/server/spi/swagger/SwaggerGenerator.java index 088c1e0c..91de7fa1 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/swagger/SwaggerGenerator.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/swagger/SwaggerGenerator.java @@ -15,7 +15,6 @@ */ package com.google.api.server.spi.swagger; -import com.google.api.server.spi.Constant; import com.google.api.server.spi.EndpointMethod; import com.google.api.server.spi.Strings; import com.google.api.server.spi.TypeLoader; @@ -149,19 +148,19 @@ public class SwaggerGenerator { .put(DateAndTime.class, "date-time") .put(Date.class, "date-time") .build(); - private static final ImmutableMap FIELD_TYPE_TO_PROPERTY_MAP = - ImmutableMap.builder() - .put(FieldType.BOOLEAN, new BooleanProperty()) - .put(FieldType.BYTE_STRING, new ByteArrayProperty()) - .put(FieldType.DATE, new DateProperty()) - .put(FieldType.DATE_TIME, new DateTimeProperty()) - .put(FieldType.DOUBLE, new DoubleProperty()) - .put(FieldType.FLOAT, new FloatProperty()) - .put(FieldType.INT8, new IntegerProperty()) - .put(FieldType.INT16, new IntegerProperty()) - .put(FieldType.INT32, new IntegerProperty()) - .put(FieldType.INT64, new LongProperty()) - .put(FieldType.STRING, new StringProperty()) + private static final ImmutableMap> FIELD_TYPE_TO_PROPERTY_CLASS_MAP = + ImmutableMap.>builder() + .put(FieldType.BOOLEAN, BooleanProperty.class) + .put(FieldType.BYTE_STRING, ByteArrayProperty.class) + .put(FieldType.DATE, DateProperty.class) + .put(FieldType.DATE_TIME, DateTimeProperty.class) + .put(FieldType.DOUBLE, DoubleProperty.class) + .put(FieldType.FLOAT, FloatProperty.class) + .put(FieldType.INT8, IntegerProperty.class) + .put(FieldType.INT16, IntegerProperty.class) + .put(FieldType.INT32, IntegerProperty.class) + .put(FieldType.INT64, LongProperty.class) + .put(FieldType.STRING, StringProperty.class) .build(); private static final Function CONFIG_TO_ROOTLESS_KEY = @@ -420,15 +419,26 @@ private Model convertToSwaggerSchema(Schema schema) { } private Property convertToSwaggerProperty(Field f) { - Property p = FIELD_TYPE_TO_PROPERTY_MAP.get(f.type()); - if (p != null) { - return p; - } else if (f.type() == FieldType.OBJECT || f.type() == FieldType.ENUM) { - return new RefProperty(f.schemaReference().get().name()); - } else if (f.type() == FieldType.ARRAY) { - return new ArrayProperty(convertToSwaggerProperty(f.arrayItemSchema())); + Property p = null; + Class propertyClass = FIELD_TYPE_TO_PROPERTY_CLASS_MAP.get(f.type()); + if (propertyClass != null) { + try { + p = propertyClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + //cannot happen, as Property subclasses are guaranteed to have a default constructor + } + } else { + if (f.type() == FieldType.OBJECT || f.type() == FieldType.ENUM) { + p = new RefProperty(f.schemaReference().get().name()); + } else if (f.type() == FieldType.ARRAY) { + p = new ArrayProperty(convertToSwaggerProperty(f.arrayItemSchema())); + } + } + if (p == null) { + throw new IllegalArgumentException("could not convert field " + f); } - throw new IllegalArgumentException("could not convert field " + f); + p.description(f.description()); + return p; } private static String getOperationId(ApiConfig apiConfig, ApiMethodConfig methodConfig) { diff --git a/endpoints-framework/src/test/java/com/google/api/server/spi/config/jsonwriter/ResourceSchemaProviderTest.java b/endpoints-framework/src/test/java/com/google/api/server/spi/config/jsonwriter/ResourceSchemaProviderTest.java index 7b82f1c9..c80e6d6b 100644 --- a/endpoints-framework/src/test/java/com/google/api/server/spi/config/jsonwriter/ResourceSchemaProviderTest.java +++ b/endpoints-framework/src/test/java/com/google/api/server/spi/config/jsonwriter/ResourceSchemaProviderTest.java @@ -72,6 +72,13 @@ public void testRenamedProperty() { assertThat(schema.getProperties().keySet()).containsExactly("bar"); } + @Test + public void testDescribedProperty() { + ResourceSchema schema = getResourceSchema(DescribedPropertyBean.class); + assertEquals("description of foo", schema.getProperties().get("foo").getDescription()); + assertEquals("description of bar", schema.getProperties().get("bar").getDescription()); + } + @Test public void testMissingPropertyType() { ResourceSchema schema = getResourceSchema(MissingPropertyTypeBean.class); @@ -183,6 +190,15 @@ public String getFoo() { } } + private static class DescribedPropertyBean { + @ApiResourceProperty(description = "description of foo") + private String foo; + @ApiResourceProperty(description = "description of bar") + public String getBar() { + return null; + } + } + /** * A JavaBean that has a JsonProperty, but no supporting JavaBean property to access it. */ diff --git a/endpoints-framework/src/test/java/com/google/api/server/spi/discovery/DiscoveryGeneratorTest.java b/endpoints-framework/src/test/java/com/google/api/server/spi/discovery/DiscoveryGeneratorTest.java index bf7f399e..9e0ae795 100644 --- a/endpoints-framework/src/test/java/com/google/api/server/spi/discovery/DiscoveryGeneratorTest.java +++ b/endpoints-framework/src/test/java/com/google/api/server/spi/discovery/DiscoveryGeneratorTest.java @@ -32,6 +32,7 @@ import com.google.api.server.spi.testing.ArrayEndpoint; import com.google.api.server.spi.testing.EnumEndpoint; import com.google.api.server.spi.testing.EnumEndpointV2; +import com.google.api.server.spi.testing.FooDescriptionEndpoint; import com.google.api.server.spi.testing.FooEndpoint; import com.google.api.server.spi.testing.MultipleParameterEndpoint; import com.google.api.server.spi.testing.NamespaceEndpoint; @@ -152,6 +153,13 @@ public void testWriteDiscovery_AbsoluteCommonPathEndpoint() throws Exception { compareDiscovery(expected, doc); } + @Test + public void testWriteDiscovery_FooEndpointWithDescription() throws Exception { + RestDescription doc = getDiscovery(context, FooDescriptionEndpoint.class); + RestDescription expected = readExpectedAsDiscovery("foo_with_description_endpoint.json"); + compareDiscovery(expected, doc); + } + @Test public void testWriteDiscovery_multipleApisWithSharedSchema() throws Exception { // Read in an API that uses a resource with fields that have their own schema, then read in diff --git a/endpoints-framework/src/test/java/com/google/api/server/spi/swagger/SwaggerGeneratorTest.java b/endpoints-framework/src/test/java/com/google/api/server/spi/swagger/SwaggerGeneratorTest.java index 2d96b50c..57d48fba 100644 --- a/endpoints-framework/src/test/java/com/google/api/server/spi/swagger/SwaggerGeneratorTest.java +++ b/endpoints-framework/src/test/java/com/google/api/server/spi/swagger/SwaggerGeneratorTest.java @@ -34,6 +34,7 @@ import com.google.api.server.spi.testing.AbsolutePathEndpoint; import com.google.api.server.spi.testing.ArrayEndpoint; import com.google.api.server.spi.testing.EnumEndpoint; +import com.google.api.server.spi.testing.FooDescriptionEndpoint; import com.google.api.server.spi.testing.FooEndpoint; import com.google.api.server.spi.testing.LimitMetricsEndpoint; import com.google.common.collect.ImmutableList; @@ -162,6 +163,14 @@ public void testWriteSwagger_LimitMetricsEndpoint() throws Exception { compareSwagger(expected, swagger); } + @Test + public void testWriteSwagger_FooEndpointWithDescription() throws Exception { + ApiConfig config = configLoader.loadConfiguration(ServiceContext.create(), FooDescriptionEndpoint.class); + Swagger swagger = generator.writeSwagger(ImmutableList.of(config), false, context); + Swagger expected = readExpectedAsSwagger("foo_with_description_endpoint.swagger"); + compareSwagger(expected, swagger); + } + private Swagger getSwagger(Class serviceClass, SwaggerContext context, boolean internal) throws Exception { ApiConfig config = configLoader.loadConfiguration(ServiceContext.create(), serviceClass); diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_with_description_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_with_description_endpoint.json new file mode 100644 index 00000000..497e5e8d --- /dev/null +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_with_description_endpoint.json @@ -0,0 +1,247 @@ +{ + "auth": { + "oauth2": { + "scopes": { + "https://www.googleapis.com/auth/userinfo.email": { + "description": "View your email address" + } + } + } + }, + "basePath": "/api/foo/v1/", + "baseUrl": "https://discovery-test.appspot.com/api/foo/v1/", + "batchPath": "batch", + "description": "Just Foo Things", + "discoveryVersion": "v1", + "icons": { + "x16": "http://www.google.com/images/icons/product/search-16.gif", + "x32": "http://www.google.com/images/icons/product/search-32.gif" + }, + "id": "foo:v1", + "kind": "discovery#restDescription", + "methods": { + "toplevel": { + "httpMethod": "POST", + "id": "foo.toplevel", + "path": "foos", + "response": { + "$ref": "CollectionResponse_FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + } + }, + "name": "foo", + "parameters": { + "alt": { + "default": "json", + "description": "Data format for the response.", + "enum": [ + "json" + ], + "enumDescriptions": [ + "Responses with Content-Type of application/json" + ], + "location": "query", + "type": "string" + }, + "fields": { + "description": "Selector specifying which fields to include in a partial response.", + "location": "query", + "type": "string" + }, + "key": { + "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.", + "location": "query", + "type": "string" + }, + "oauth_token": { + "description": "OAuth 2.0 token for the current user.", + "location": "query", + "type": "string" + }, + "prettyPrint": { + "default": "true", + "description": "Returns response with indentations and line breaks.", + "location": "query", + "type": "boolean" + }, + "quotaUser": { + "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.", + "location": "query", + "type": "string" + }, + "userIp": { + "description": "IP address of the site where the request originates. Use this if you want to enforce per-user limits.", + "location": "query", + "type": "string" + } + }, + "protocol": "rest", + "resources": { + "foo": { + "methods": { + "create": { + "description": "create desc", + "httpMethod": "PUT", + "id": "foo.foo.create", + "parameterOrder": [ + "id" + ], + "parameters": { + "id": { + "description": "id desc", + "location": "path", + "required": true, + "type": "string" + } + }, + "path": "foos/{id}", + "request": { + "$ref": "FooDescription", + "parameterName": "resource" + }, + "response": { + "$ref": "FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + }, + "delete": { + "description": "delete desc", + "httpMethod": "DELETE", + "id": "foo.foo.delete", + "parameterOrder": [ + "id" + ], + "parameters": { + "id": { + "description": "id desc", + "location": "path", + "required": true, + "type": "string" + } + }, + "path": "foos/{id}", + "response": { + "$ref": "FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + }, + "get": { + "description": "get desc", + "httpMethod": "GET", + "id": "foo.foo.get", + "parameterOrder": [ + "id" + ], + "parameters": { + "id": { + "description": "id desc", + "location": "path", + "required": true, + "type": "string" + } + }, + "path": "foos/{id}", + "response": { + "$ref": "FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + }, + "list": { + "description": "list desc", + "httpMethod": "GET", + "id": "foo.foo.list", + "parameterOrder": [ + "n" + ], + "parameters": { + "n": { + "format": "int32", + "location": "query", + "required": true, + "type": "integer" + } + }, + "path": "foos", + "response": { + "$ref": "CollectionResponse_FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + }, + "update": { + "description": "update desc", + "httpMethod": "POST", + "id": "foo.foo.update", + "parameterOrder": [ + "id" + ], + "parameters": { + "id": { + "description": "id desc", + "location": "path", + "required": true, + "type": "string" + } + }, + "path": "foos/{id}", + "request": { + "$ref": "FooDescription", + "parameterName": "resource" + }, + "response": { + "$ref": "FooDescription" + }, + "scopes": [ + "https://www.googleapis.com/auth/userinfo.email" + ] + } + } + } + }, + "rootUrl": "https://discovery-test.appspot.com/api/", + "schemas": { + "CollectionResponse_FooDescription": { + "id": "CollectionResponse_FooDescription", + "properties": { + "items": { + "items": { + "$ref": "FooDescription" + }, + "type": "array" + }, + "nextPageToken": { + "type": "string" + } + }, + "type": "object" + }, + "FooDescription": { + "id": "FooDescription", + "properties": { + "name": { + "description":"description of name", + "type": "string" + }, + "value": { + "description":"description of value", + "format": "int32", + "type": "integer" + } + }, + "type": "object" + } + }, + "servicePath": "foo/v1/", + "title": "The Foo API", + "version": "v1" +} diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/swagger/foo_with_description_endpoint.swagger b/endpoints-framework/src/test/resources/com/google/api/server/spi/swagger/foo_with_description_endpoint.swagger new file mode 100644 index 00000000..d2e4bca2 --- /dev/null +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/swagger/foo_with_description_endpoint.swagger @@ -0,0 +1,251 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "swagger-test.appspot.com" + }, + "host": "swagger-test.appspot.com", + "basePath": "/api", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/foo/v1/foos": { + "get": { + "description": "list desc", + "operationId": "FooListFoos", + "parameters": [ + { + "name": "n", + "in": "query", + "required": true, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/CollectionResponse_FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + }, + "post": { + "operationId": "FooToplevel", + "parameters": [], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/CollectionResponse_FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + } + }, + "/foo/v1/foos/{id}": { + "get": { + "description": "get desc", + "operationId": "FooGetFoo", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "id desc", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + }, + "post": { + "description": "update desc", + "operationId": "FooUpdateFoo", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "id desc", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": false, + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + ], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + }, + "put": { + "description": "create desc", + "operationId": "FooCreateFoo", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "id desc", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": false, + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + ], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + }, + "delete": { + "description": "delete desc", + "operationId": "FooDeleteFoo", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "id desc", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "A successful response", + "schema": { + "$ref": "#/definitions/FooDescription" + } + } + }, + "security": [ + { + "google_id_token-3a26ea04": [] + }, + { + "google_id_token_https-3a26ea04": [] + } + ] + } + } + }, + "securityDefinitions": { + "google_id_token-3a26ea04": { + "type": "oauth2", + "authorizationUrl": "", + "flow": "implicit", + "x-google-issuer": "accounts.google.com", + "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v1/certs", + "x-google-audiences": "audience" + }, + "google_id_token_https-3a26ea04": { + "type": "oauth2", + "authorizationUrl": "", + "flow": "implicit", + "x-google-issuer": "https://accounts.google.com", + "x-google-jwks_uri": "https://www.googleapis.com/oauth2/v1/certs", + "x-google-audiences": "audience" + } + }, + "definitions": { + "CollectionResponse_FooDescription": { + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/FooDescription" + } + }, + "nextPageToken": { + "type": "string" + } + } + }, + "FooDescription": { + "properties": { + "name": { + "description": "description of name", + "type": "string" + }, + "value": { + "type": "integer", + "format": "int32", + "description": "description of value" + } + } + } + } +} diff --git a/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescription.java b/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescription.java new file mode 100644 index 00000000..38ec954b --- /dev/null +++ b/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescription.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Google Inc. 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 com.google.api.server.spi.testing; + +import com.google.api.server.spi.config.ApiResourceProperty; + +/** + * Test resource type with descriptions. + */ +public class FooDescription { + + @ApiResourceProperty(description = "description of name") + private String name; + private int value; + private String hidden; + + public String getName() { + return name; + } + + @ApiResourceProperty(description = "description of value") + public int getValue() { + return value; + } + + private String getHidden() { + return hidden; + } + + private void setHidden(String hidden) { + this.hidden = hidden; + } +} diff --git a/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescriptionEndpoint.java b/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescriptionEndpoint.java new file mode 100644 index 00000000..66816d9a --- /dev/null +++ b/test-utils/src/main/java/com/google/api/server/spi/testing/FooDescriptionEndpoint.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Google Inc. 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 com.google.api.server.spi.testing; + +import com.google.api.server.spi.config.Api; +import com.google.api.server.spi.config.ApiMethod; +import com.google.api.server.spi.config.ApiMethod.HttpMethod; +import com.google.api.server.spi.config.Description; +import com.google.api.server.spi.config.Named; +import com.google.api.server.spi.response.CollectionResponse; + +@Api( + name = "foo", + version = "v1", + audiences = {"audience"}, + title = "The Foo API", + description = "Just Foo Things") +public class FooDescriptionEndpoint { + @ApiMethod(name = "foo.create", description = "create desc", path = "foos/{id}", + httpMethod = HttpMethod.PUT) + public FooDescription createFoo(@Named("id") @Description("id desc") String id, FooDescription foo) { + return null; + } + @ApiMethod(name = "foo.get", description = "get desc", path = "foos/{id}", + httpMethod = HttpMethod.GET) + public FooDescription getFoo(@Named("id") @Description("id desc") String id) { + return null; + } + @ApiMethod(name = "foo.update", description = "update desc", path = "foos/{id}", + httpMethod = HttpMethod.POST) + public FooDescription updateFoo(@Named("id") @Description("id desc") String id, FooDescription foo) { + return null; + } + @ApiMethod(name = "foo.delete", description = "delete desc", path = "foos/{id}", + httpMethod = HttpMethod.DELETE) + public FooDescription deleteFoo(@Named("id") @Description("id desc") String id) { + return null; + } + @ApiMethod(name = "foo.list", description = "list desc", path = "foos", + httpMethod = HttpMethod.GET) + public CollectionResponse listFoos(@Named("n") Integer n) { + return null; + } + @ApiMethod(name = "toplevel", path = "foos", httpMethod = HttpMethod.POST) + public CollectionResponse toplevel() { + return null; + } +}