diff --git a/endpoints-framework-tools/src/main/resources/com/google/api/server/spi/tools/testing/fake-discovery-doc-rest.json b/endpoints-framework-tools/src/main/resources/com/google/api/server/spi/tools/testing/fake-discovery-doc-rest.json index f5c4ffb5..3bd712c6 100644 --- a/endpoints-framework-tools/src/main/resources/com/google/api/server/spi/tools/testing/fake-discovery-doc-rest.json +++ b/endpoints-framework-tools/src/main/resources/com/google/api/server/spi/tools/testing/fake-discovery-doc-rest.json @@ -5,8 +5,8 @@ "version": "v1", "description": "App Engine GuestBook API", "icons": { - "x16": "http://www.google.com/images/icons/product/search-16.gif", - "x32": "http://www.google.com/images/icons/product/search-32.gif" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "labels": [ "labs" diff --git a/endpoints-framework-tools/src/test/java/com/google/api/server/spi/tools/AnnotationApiConfigGeneratorTest.java b/endpoints-framework-tools/src/test/java/com/google/api/server/spi/tools/AnnotationApiConfigGeneratorTest.java index 91917dcc..e4fce432 100644 --- a/endpoints-framework-tools/src/test/java/com/google/api/server/spi/tools/AnnotationApiConfigGeneratorTest.java +++ b/endpoints-framework-tools/src/test/java/com/google/api/server/spi/tools/AnnotationApiConfigGeneratorTest.java @@ -2528,6 +2528,18 @@ class ApiWithDocs { assertEquals("http://go/documentation", root.path("documentation").asText()); } + @Test + public void testIcons() throws Exception { + @Api(name = "myapi", iconX16 = "http://go/iconX16", iconX32 = "http://go/iconX32") + class ApiWithIcons { + } + String apiConfigSource = g.generateConfig(ApiWithIcons.class).get("myapi-v1.api"); + ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); + assertEquals("myapi", root.path("name").asText()); + assertEquals("http://go/iconX16", root.path("icons").path("x16").asText()); + assertEquals("http://go/iconX32", root.path("icons").path("x32").asText()); + } + @Test public void testGenericParameterTypes() throws Exception { @Api diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/Api.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/Api.java index 8bcdd0dd..57628641 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/Api.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/Api.java @@ -72,6 +72,16 @@ */ String documentationLink() default ""; + /** + * A link to a 16x16 icon representing the API. + */ + String iconX16() default ""; + + /** + * A link to a 32x32 icon representing the API. + */ + String iconX32() default ""; + /** * Backend root URL, e.g. "https://example.appspot.com/_ah/spi". This is the root of all backend * method calls. This will default to "https://yourapp.appspot.com/_ah/spi". Non-secure http URLs diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationConfig.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationConfig.java index 017ed0cc..2487c9c1 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationConfig.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiAnnotationConfig.java @@ -89,6 +89,18 @@ public void setDocumentationLinkIfNotEmpty(String documentationLink) { } } + public void setIconX16IfNotEmpty(String iconX16) { + if (!Strings.isNullOrEmpty(iconX16)) { + config.setIconX16(iconX16); + } + } + + public void setIconX32IfNotEmpty(String iconX32) { + if (!Strings.isNullOrEmpty(iconX32)) { + config.setIconX32(iconX32); + } + } + public void setBackendRootIfNotEmpty(String backendRoot) { if (!Strings.isNullOrEmpty(backendRoot)) { config.setBackendRoot(backendRoot); diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiConfigAnnotationReader.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiConfigAnnotationReader.java index 27c6541f..7c80e439 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiConfigAnnotationReader.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/annotationreader/ApiConfigAnnotationReader.java @@ -188,6 +188,8 @@ private void readApi(ApiAnnotationConfig config, Annotation api) config.setTitleIfNotEmpty((String) getAnnotationProperty(api, "title")); config.setDescriptionIfNotEmpty((String) getAnnotationProperty(api, "description")); config.setDocumentationLinkIfNotEmpty((String) getAnnotationProperty(api, "documentationLink")); + config.setIconX16IfNotEmpty((String) getAnnotationProperty(api, "iconX16")); + config.setIconX32IfNotEmpty((String) getAnnotationProperty(api, "iconX32")); config.setIsDefaultVersionIfSpecified( (AnnotationBoolean) getAnnotationProperty(api, "defaultVersion")); config.setIsDiscoverableIfSpecified( diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JsonConfigWriter.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JsonConfigWriter.java index 25980885..b003a005 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JsonConfigWriter.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/jsonwriter/JsonConfigWriter.java @@ -171,6 +171,12 @@ private void convertApi(ObjectNode root, ApiConfig config) { if (config.getDocumentationLink() != null) { root.put("documentation", config.getDocumentationLink()); } + if (config.getIconX16() != null || config.getIconX32() != null) { + ObjectNode icons = objectMapper.createObjectNode(); + icons.put("x16", config.getIconX16()); + icons.put("x32", config.getIconX32()); + root.set("icons", icons); + } root.put("defaultVersion", config.getIsDefaultVersion()); ArrayNode discovery = objectMapper.createArrayNode(); discovery.add(config.getIsDiscoverable() ? "PUBLIC" : "OFF"); diff --git a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiConfig.java b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiConfig.java index a76b26bc..f0312838 100644 --- a/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiConfig.java +++ b/endpoints-framework/src/main/java/com/google/api/server/spi/config/model/ApiConfig.java @@ -59,6 +59,8 @@ public class ApiConfig { private String title; private String description; private String documentationLink; + private String iconX16; + private String iconX32; private String backendRoot; private boolean isAbstract; private boolean defaultVersion; @@ -128,6 +130,8 @@ protected ApiConfig(ApiConfig original) { this.title = original.title; this.description = original.description; this.documentationLink = original.documentationLink; + this.iconX16 = original.iconX16; + this.iconX32 = original.iconX32; this.backendRoot = original.backendRoot; this.isAbstract = original.isAbstract; this.defaultVersion = original.defaultVersion; @@ -180,6 +184,8 @@ public Iterable> getConfigurationInconsistencies( .addIfInconsistent("title", title, config.title) .addIfInconsistent("description", description, config.description) .addIfInconsistent("documentationLink", documentationLink, config.documentationLink) + .addIfInconsistent("iconX16", iconX16, config.iconX16) + .addIfInconsistent("iconX32", iconX32, config.iconX32) .addIfInconsistent("backendRoot", backendRoot, config.backendRoot) .addIfInconsistent("isAbstract", isAbstract, config.isAbstract) .addIfInconsistent("defaultVersion", defaultVersion, config.defaultVersion) @@ -207,8 +213,8 @@ public Iterable> getConfigurationInconsistencies( @Override public int hashCode() { return Objects.hash(typeLoader, root, name, canonicalName, version, title, description, - documentationLink, backendRoot, isAbstract, defaultVersion, discoverable, useDatastore, - resource, authLevel, scopeExpression, audiences, clientIds, authenticators, + documentationLink, iconX16, iconX32, backendRoot, isAbstract, defaultVersion, discoverable, + useDatastore, resource, authLevel, scopeExpression, audiences, clientIds, authenticators, peerAuthenticators, authConfig, cacheControlConfig, frontendLimitsConfig, serializationConfig, apiClassConfig, issuers, issuerAudiences, apiKeyRequired, apiLimitMetrics); @@ -344,6 +350,22 @@ public String getDocumentationLink() { return documentationLink; } + public void setIconX16(String iconX16) { + this.iconX16 = iconX16; + } + + public String getIconX16() { + return iconX16; + } + + public void setIconX32(String iconX32) { + this.iconX32 = iconX32; + } + + public String getIconX32() { + return iconX32; + } + public void setBackendRoot(String backendRoot) { this.backendRoot = toHttps(backendRoot); } 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..19cc6a69 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 @@ -86,8 +86,8 @@ public class DiscoveryGenerator { .setDescription("This is an API") .setDiscoveryVersion("v1") .setIcons(new RestDescription.Icons() - .setX16("http://www.google.com/images/icons/product/search-16.gif") - .setX32("http://www.google.com/images/icons/product/search-32.gif")) + .setX16("https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png") + .setX32("https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png")) .setKind("discovery#restDescription") .setParameters(createStandardParameters()) .setProtocol("rest"); @@ -151,6 +151,12 @@ private RestDescription writeApi(ApiKey apiKey, Iterable apiConfigs, if (config.getDescription() != null) { doc.setDescription(config.getDescription()); } + if (config.getIconX16() != null) { + doc.getIcons().setX16(config.getIconX16()); + } + if (config.getIconX32() != null) { + doc.getIcons().setX32(config.getIconX32()); + } if (config.getTitle() != null) { doc.setTitle(config.getTitle()); } @@ -385,8 +391,8 @@ private DirectoryList generateDirectory(Map discoveryDo .setDiscoveryLink("." + relativePath) .setDiscoveryRestUrl(context.getApiRoot() + "/discovery/v1" + relativePath) .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")) + .setX16(doc.getIcons().getX16()) + .setX32(doc.getIcons().getX32())) .setId(doc.getName() + ":" + doc.getVersion()) .setKind("discovery#directoryItem") .setName(doc.getName()) diff --git a/endpoints-framework/src/test/java/com/google/api/server/spi/config/model/ApiConfigTest.java b/endpoints-framework/src/test/java/com/google/api/server/spi/config/model/ApiConfigTest.java index 500e378f..cd3c45f5 100644 --- a/endpoints-framework/src/test/java/com/google/api/server/spi/config/model/ApiConfigTest.java +++ b/endpoints-framework/src/test/java/com/google/api/server/spi/config/model/ApiConfigTest.java @@ -159,6 +159,8 @@ public void testCopyConstructor() { apiConfig.setTitle("title"); apiConfig.setDescription("desc"); apiConfig.setDocumentationLink("link"); + apiConfig.setIconX16("iconX16"); + apiConfig.setIconX32("iconX32"); apiConfig.setBackendRoot("backend"); apiConfig.setIsAbstract(true); apiConfig.setIsDefaultVersion(true); diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_common_path_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_common_path_endpoint.json index 8e16731e..857a23e6 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_common_path_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_common_path_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "absolutepath:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_path_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_path_endpoint.json index 20f28211..a0a41bf1 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_path_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/absolute_path_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "absolutepath:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/array_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/array_endpoint.json index 42ac3489..da404334 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/array_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/array_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "myapi:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/directory.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/directory.json index ec3c10d6..e7a72653 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/directory.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/directory.json @@ -6,8 +6,8 @@ "discoveryLink": "./apis/myapi/v1/rest", "discoveryRestUrl": "https://myapi.appspot.com/_ah/api/discovery/v1/apis/myapi/v1/rest", "icons": { - "x16": "http://www.google.com/images/icons/product/search-16.gif", - "x32": "http://www.google.com/images/icons/product/search-32.gif" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "myapi:v1", "kind": "discovery#directoryItem", @@ -20,8 +20,8 @@ "discoveryLink": "./apis/enum/v1/rest", "discoveryRestUrl": "https://myapi.appspot.com/_ah/api/discovery/v1/apis/enum/v1/rest", "icons": { - "x16": "http://www.google.com/images/icons/product/search-16.gif", - "x32": "http://www.google.com/images/icons/product/search-32.gif" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "enum:v1", "kind": "discovery#directoryItem", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/enum_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/enum_endpoint.json index 2584c42c..5ef91688 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/enum_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/enum_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "enum:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint.json index 919211ab..acaf574b 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint.json @@ -14,8 +14,8 @@ "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "foo:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_default_context.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_default_context.json index 8b5df65d..baf28645 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_default_context.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_default_context.json @@ -14,8 +14,8 @@ "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "foo:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_localhost.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_localhost.json index 54f35fc0..0f9a2d01 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_localhost.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/foo_endpoint_localhost.json @@ -14,8 +14,8 @@ "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "foo:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/multiple_parameter_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/multiple_parameter_endpoint.json index cfb6123f..a51186f9 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/multiple_parameter_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/multiple_parameter_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "multipleparam:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/namespace_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/namespace_endpoint.json index 9a957fce..17edbfea 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/namespace_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/namespace_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "namespace:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/primitive_endpoint.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/primitive_endpoint.json index 42a59e7c..e3ccc382 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/primitive_endpoint.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/primitive_endpoint.json @@ -14,8 +14,8 @@ "description": "This is an API", "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" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "myapi:v1", "kind": "discovery#restDescription", diff --git a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/tictactoe.json b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/tictactoe.json index 4c96941e..123292a5 100644 --- a/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/tictactoe.json +++ b/endpoints-framework/src/test/resources/com/google/api/server/spi/discovery/tictactoe.json @@ -15,8 +15,8 @@ "discoveryVersion": "v1", "etag": "\"zRMgE0l9nVDW4S28VYYcYQF9UW4/JGTFfoDuVVdzoKDgyqpFMu-y3OU\"", "icons": { - "x16": "http://www.google.com/images/icons/product/search-16.gif", - "x32": "http://www.google.com/images/icons/product/search-32.gif" + "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png", + "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png" }, "id": "tictactoe:v1", "kind": "discovery#restDescription",