diff --git a/src/main/java/com/google/api/codegen/discovery/Document.java b/src/main/java/com/google/api/codegen/discovery/Document.java index f788e56f30..77ea38dba6 100644 --- a/src/main/java/com/google/api/codegen/discovery/Document.java +++ b/src/main/java/com/google/api/codegen/discovery/Document.java @@ -14,6 +14,7 @@ */ package com.google.api.codegen.discovery; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.auto.value.AutoValue; import java.util.ArrayList; @@ -21,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; /** * A representation of a Discovery Document. @@ -29,7 +31,7 @@ * example, this class combines all methods in the Discovery Document into one list for convenience. */ @AutoValue -public abstract class Document { +public abstract class Document implements Node { // TODO(saicheems): Assert that all references link to a valid schema? @@ -54,8 +56,9 @@ public static Document from(DiscoveryNode root) { } String canonicalName = root.getString("canonicalName"); String description = root.getString("description"); + String id = root.getString("id"); Map schemas = parseSchemas(root); - List methods = parseMethods(root, ""); + List methods = parseMethods(root); Collections.sort(methods); // Ensure methods are ordered alphabetically by their ID. String name = root.getString("name"); if (canonicalName.isEmpty()) { @@ -68,21 +71,32 @@ public static Document from(DiscoveryNode root) { String version = root.getString("version"); boolean versionModule = root.getBoolean("version_module"); - return new AutoValue_Document( - "", // authInstructionsUrl (only intended to be overridden). - authType, - canonicalName, - description, - "", // discoveryDocUrl (only intended to be overridden). - methods, - name, - revision, - rootUrl, - schemas, - servicePath, - title, - version, - versionModule); + Document thisDocument = + new AutoValue_Document( + "", // authInstructionsUrl (only intended to be overridden). + authType, + canonicalName, + description, + "", // discoveryDocUrl (only intended to be overridden). + id, + methods, + name, + revision, + rootUrl, + schemas, + servicePath, + title, + version, + versionModule); + + for (Schema schema : schemas.values()) { + schema.setParent(thisDocument); + } + for (Method method : methods) { + method.setParent(thisDocument); + } + + return thisDocument; } /** @@ -105,20 +119,17 @@ public Schema dereferenceSchema(Schema schema) { return schema; } - private static List parseMethods(DiscoveryNode root, String path) { + private static List parseMethods(DiscoveryNode root) { List methods = new ArrayList<>(); DiscoveryNode methodsNode = root.getObject("methods"); List resourceNames = methodsNode.getFieldNames(); - if (!path.isEmpty()) { - path += "."; - } for (String name : resourceNames) { - methods.add(Method.from(methodsNode.getObject(name), path + "methods." + name)); + methods.add(Method.from(methodsNode.getObject(name), null)); } DiscoveryNode resourcesNode = root.getObject("resources"); resourceNames = resourcesNode.getFieldNames(); for (String name : resourceNames) { - methods.addAll(parseMethods(resourcesNode.getObject(name), path + "resources." + name)); + methods.addAll(parseMethods(resourcesNode.getObject(name))); } return methods; } @@ -127,11 +138,23 @@ private static Map parseSchemas(DiscoveryNode root) { Map schemas = new HashMap<>(); DiscoveryNode schemasNode = root.getObject("schemas"); for (String name : schemasNode.getFieldNames()) { - schemas.put(name, Schema.from(schemasNode.getObject(name), "schemas." + name)); + schemas.put(name, Schema.from(schemasNode.getObject(name), null)); } return schemas; } + /** @return the parent Node that contains this node. */ + @JsonIgnore @Nullable private Node parent; + + public Node parent() { + return parent; + } + + // Package private for use by other Node objects. + void setParent(Node parent) { + this.parent = parent; + } + /** @return the auth instructions URL. */ @JsonProperty("authInstructionsUrl") public abstract String authInstructionsUrl(); @@ -152,6 +175,11 @@ private static Map parseSchemas(DiscoveryNode root) { @JsonProperty("discoveryDocUrl") public abstract String discoveryDocUrl(); + /** @return the ID of the Discovery document for the API. */ + @Override + @JsonProperty("id") + public abstract String id(); + /** @return the list of all methods. */ @JsonProperty("methods") public abstract List methods(); diff --git a/src/main/java/com/google/api/codegen/discovery/Method.java b/src/main/java/com/google/api/codegen/discovery/Method.java index 1168c571a1..d7ddbff1d6 100644 --- a/src/main/java/com/google/api/codegen/discovery/Method.java +++ b/src/main/java/com/google/api/codegen/discovery/Method.java @@ -28,16 +28,15 @@ *

Note that this class is not necessarily a 1-1 mapping of the official specification. */ @AutoValue -public abstract class Method implements Comparable { +public abstract class Method implements Comparable, Node { /** * Returns a method constructed from root. * * @param root the root node to parse. - * @param path the full path to this node (ex: "resources.foo.methods.bar"). * @return a method. */ - public static Method from(DiscoveryNode root, String path) { + public static Method from(DiscoveryNode root, Node parent) { String description = root.getString("description"); String httpMethod = root.getString("httpMethod"); String id = root.getString("id"); @@ -49,7 +48,7 @@ public static Method from(DiscoveryNode root, String path) { DiscoveryNode parametersNode = root.getObject("parameters"); HashMap parameters = new HashMap<>(); for (String name : root.getObject("parameters").getFieldNames()) { - Schema schema = Schema.from(parametersNode.getObject(name), path + ".parameters." + name); + Schema schema = Schema.from(parametersNode.getObject(name), null); // TODO: Remove these checks once we're sure that parameters can't be objects/arrays. // This is based on the assumption that these types can't be serialized as a query or path parameter. Preconditions.checkState(schema.type() != Schema.Type.ANY); @@ -58,11 +57,11 @@ public static Method from(DiscoveryNode root, String path) { parameters.put(name, schema); } - Schema request = Schema.from(root.getObject("request"), path + ".request"); + Schema request = Schema.from(root.getObject("request"), null); if (request.reference().isEmpty()) { request = null; } - Schema response = Schema.from(root.getObject("response"), path + ".response"); + Schema response = Schema.from(root.getObject("response"), null); if (response.reference().isEmpty()) { response = null; } @@ -73,18 +72,30 @@ public static Method from(DiscoveryNode root, String path) { boolean supportsMediaDownload = root.getBoolean("supportsMediaDownload"); boolean supportsMediaUpload = root.getBoolean("supportsMediaUpload"); - return new AutoValue_Method( - description, - httpMethod, - id, - parameterOrder, - parameters, - path, - request, - response, - scopes, - supportsMediaDownload, - supportsMediaUpload); + Method thisMethod = + new AutoValue_Method( + description, + httpMethod, + id, + parameterOrder, + parameters, + request, + response, + scopes, + supportsMediaDownload, + supportsMediaUpload); + + thisMethod.parent = parent; + if (request != null) { + request.setParent(thisMethod); + } + if (response != null) { + response.setParent(thisMethod); + } + for (Schema schema : parameters.values()) { + schema.setParent(thisMethod); + } + return thisMethod; } @Override @@ -92,6 +103,17 @@ public int compareTo(Method other) { return id().compareTo(other.id()); } + /** @return the parent Node. */ + private Node parent; + + void setParent(Node parent) { + this.parent = parent; + } + + public Node parent() { + return parent; + } + /** @return the description. */ public abstract String description(); @@ -107,9 +129,6 @@ public int compareTo(Method other) { /** @return the map of parameter names to schemas. */ public abstract Map parameters(); - /** @return the fully qualified path to this method. */ - public abstract String path(); - /** @return the request schema, or null if none. */ @Nullable public abstract Schema request(); diff --git a/src/main/java/com/google/api/codegen/discovery/Node.java b/src/main/java/com/google/api/codegen/discovery/Node.java new file mode 100644 index 0000000000..53ad496622 --- /dev/null +++ b/src/main/java/com/google/api/codegen/discovery/Node.java @@ -0,0 +1,28 @@ +/* Copyright 2017 Google Inc + * + * 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.codegen.discovery; + +import javax.annotation.Nullable; + +/** Represents a node in a tree of nodes. */ +public interface Node { + /** @return the ID of this node. */ + @Nullable + String id(); + + /** @return the immediate parent of this node. */ + @Nullable + Node parent(); +} diff --git a/src/main/java/com/google/api/codegen/discovery/Schema.java b/src/main/java/com/google/api/codegen/discovery/Schema.java index 136b325b4c..f19ff89ee5 100644 --- a/src/main/java/com/google/api/codegen/discovery/Schema.java +++ b/src/main/java/com/google/api/codegen/discovery/Schema.java @@ -14,6 +14,7 @@ */ package com.google.api.codegen.discovery; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.auto.value.AutoValue; import java.util.HashMap; import java.util.Map; @@ -25,7 +26,7 @@ *

Note that this class is not necessarily a 1-1 mapping of the official specification. */ @AutoValue -public abstract class Schema { +public abstract class Schema implements Node { /** * Returns true if this schema contains a property with the given name. @@ -41,15 +42,13 @@ public boolean hasProperty(String name) { * Returns a schema constructed from root, or an empty schema if root has no children. * * @param root the root node to parse. - * @param path the full path to this node (ex: "methods.foo.parameters.bar"). * @return a schema. */ - public static Schema from(DiscoveryNode root, String path) { + public static Schema from(DiscoveryNode root, Node parent) { if (root.isEmpty()) { return empty(); } - Schema additionalProperties = - Schema.from(root.getObject("additionalProperties"), path + ".additionalProperties"); + Schema additionalProperties = Schema.from(root.getObject("additionalProperties"), null); if (additionalProperties.type() == Type.EMPTY && additionalProperties.reference().isEmpty()) { additionalProperties = null; } @@ -58,7 +57,7 @@ public static Schema from(DiscoveryNode root, String path) { Format format = Format.getEnum(root.getString("format")); String id = root.getString("id"); boolean isEnum = !root.getArray("enum").isEmpty(); - Schema items = Schema.from(root.getObject("items"), path + ".items"); + Schema items = Schema.from(root.getObject("items"), null); if (items.type() == Type.EMPTY && items.reference().isEmpty()) { items = null; } @@ -68,8 +67,7 @@ public static Schema from(DiscoveryNode root, String path) { Map properties = new HashMap<>(); DiscoveryNode propertiesNode = root.getObject("properties"); for (String name : propertiesNode.getFieldNames()) { - properties.put( - name, Schema.from(propertiesNode.getObject(name), path + ".properties." + name)); + properties.put(name, Schema.from(propertiesNode.getObject(name), null)); } String reference = root.getString("$ref"); @@ -77,22 +75,34 @@ public static Schema from(DiscoveryNode root, String path) { boolean required = root.getBoolean("required"); Type type = Type.getEnum(root.getString("type")); - return new AutoValue_Schema( - additionalProperties, - defaultValue, - description, - format, - id, - isEnum, - items, - location, - path, - pattern, - properties, - reference, - repeated, - required, - type); + Schema thisSchema = + new AutoValue_Schema( + additionalProperties, + defaultValue, + description, + format, + id, + isEnum, + items, + location, + pattern, + properties, + reference, + repeated, + required, + type); + thisSchema.parent = parent; + if (items != null) { + items.setParent(thisSchema); + } + for (Schema schema : properties.values()) { + schema.setParent(thisSchema); + } + if (additionalProperties != null) { + additionalProperties.setParent(thisSchema); + } + + return thisSchema; } public static Schema empty() { @@ -106,7 +116,6 @@ public static Schema empty() { null, "", "", - "", new HashMap(), "", false, @@ -114,6 +123,18 @@ public static Schema empty() { Type.EMPTY); } + @JsonIgnore @Nullable private Node parent; + + /** @return the {@link Node} that contains this Schema. */ + @Nullable + public Node parent() { + return parent; + } + + void setParent(Node parent) { + this.parent = parent; + } + /** @return the schema of the additionalProperties, or null if none. */ @Nullable public abstract Schema additionalProperties(); @@ -142,9 +163,6 @@ public static Schema empty() { /** @return the location. */ public abstract String location(); - /** @return the fully qualified path to this schema. */ - public abstract String path(); - /** @return the pattern. */ public abstract String pattern(); diff --git a/src/test/java/com/google/api/codegen/discovery/DocumentTest.java b/src/test/java/com/google/api/codegen/discovery/DocumentTest.java index 1674e24cb4..837b1363b8 100644 --- a/src/test/java/com/google/api/codegen/discovery/DocumentTest.java +++ b/src/test/java/com/google/api/codegen/discovery/DocumentTest.java @@ -52,21 +52,22 @@ public void testDocumentFromJson() throws IOException { Truth.assertThat(methods.get(0).description()).isEqualTo("Get a baz."); Truth.assertThat(methods.get(0).id()).isEqualTo("myapi.bar.baz.get"); Truth.assertThat(methods.get(0).parameterOrder()).isEqualTo(Collections.singletonList("p1")); - Truth.assertThat(methods.get(0).path()).isEqualTo("resources.bar.methods.baz"); + Truth.assertThat(methods.get(0).parent() instanceof Document); + Truth.assertThat(methods.get(0).parent()).isEqualTo(document); Map parameters = methods.get(0).parameters(); Truth.assertThat(parameters.get("p1").type()).isEqualTo(Schema.Type.BOOLEAN); Truth.assertThat(parameters.get("p1").required()).isTrue(); Truth.assertThat(parameters.get("p1").location()).isEqualTo("query"); - Truth.assertThat(parameters.get("p1").path()) - .isEqualTo("resources.bar.methods.baz.parameters.p1"); + Truth.assertThat(parameters.get("p1").parent() instanceof Method); + Truth.assertThat(parameters.get("p1").parent()).isEqualTo(methods.get(0)); Truth.assertThat(methods.get(1).description()).isEqualTo("Insert a foo."); Truth.assertThat(methods.get(1).id()).isEqualTo("myapi.foo.insert"); Truth.assertThat(methods.get(1).parameters().isEmpty()).isTrue(); Truth.assertThat(methods.get(1).request()).isNull(); Truth.assertThat(methods.get(1).response()).isNull(); - Truth.assertThat(methods.get(1).path()).isEqualTo("methods.foo"); + Truth.assertThat(methods.get(1).id()).isEqualTo("myapi.foo.insert"); Truth.assertThat(document.name()).isEqualTo("myapi"); Truth.assertThat(document.canonicalName()).isEqualTo("My API"); @@ -76,9 +77,10 @@ public void testDocumentFromJson() throws IOException { Map schemas = document.schemas(); Truth.assertThat(schemas.get("GetBazRequest").type()).isEqualTo(Schema.Type.STRING); - Truth.assertThat(schemas.get("GetBazRequest").path()).isEqualTo("schemas.GetBazRequest"); - Truth.assertThat(schemas.get("GetBazRequest").properties().get("foo").path()) - .isEqualTo("schemas.GetBazRequest.properties.foo"); + Truth.assertThat(schemas.get("GetBazRequest").properties().get("foo").parent()) + .isEqualTo(schemas.get("GetBazRequest")); + Truth.assertThat(schemas.get("GetBazRequest").parent() instanceof Document); + Truth.assertThat(schemas.get("GetBazRequest").parent()).isEqualTo(document); Truth.assertThat(schemas.get("Baz").type()).isEqualTo(Schema.Type.STRING); diff --git a/src/test/java/com/google/api/codegen/discovery/MethodTest.java b/src/test/java/com/google/api/codegen/discovery/MethodTest.java index dc74e6b206..837dceb492 100644 --- a/src/test/java/com/google/api/codegen/discovery/MethodTest.java +++ b/src/test/java/com/google/api/codegen/discovery/MethodTest.java @@ -35,7 +35,7 @@ public void testMethodFromJson() throws IOException { ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(reader); - Method method = Method.from(new DiscoveryNode(root), "root"); + Method method = Method.from(new DiscoveryNode(root), null); Truth.assertThat(method.description()).isEqualTo("Get a baz!"); Truth.assertThat(method.httpMethod()).isEqualTo("GET"); @@ -46,7 +46,7 @@ public void testMethodFromJson() throws IOException { Truth.assertThat(parameters.get("p1").type()).isEqualTo(Schema.Type.STRING); Truth.assertThat(parameters.get("p1").required()).isTrue(); Truth.assertThat(parameters.get("p1").location()).isEqualTo("path"); - Truth.assertThat(parameters.get("p1").path()).isEqualTo("root.parameters.p1"); + Truth.assertThat(parameters.get("p1").parent().id()).isEqualTo("foo.bar.baz.get"); Truth.assertThat(parameters.get("p2").type()).isEqualTo(Schema.Type.STRING); Truth.assertThat(parameters.get("p2").location()).isEqualTo("query"); @@ -57,8 +57,8 @@ public void testMethodFromJson() throws IOException { Truth.assertThat(method.request().reference()).isEqualTo("GetBazRequest"); Truth.assertThat(method.response().reference()).isEqualTo("Baz"); - Truth.assertThat(method.request().path()).isEqualTo("root.request"); - Truth.assertThat(method.response().path()).isEqualTo("root.response"); + Truth.assertThat(method.request().parent().id()).isEqualTo("foo.bar.baz.get"); + Truth.assertThat(method.response().parent().id()).isEqualTo("foo.bar.baz.get"); Truth.assertThat(method.scopes()) .isEqualTo(Arrays.asList("https://www.example.com/foo", "https://www.example.com/bar")); diff --git a/src/test/java/com/google/api/codegen/discovery/SchemaTest.java b/src/test/java/com/google/api/codegen/discovery/SchemaTest.java index aa22ec5beb..2769840350 100644 --- a/src/test/java/com/google/api/codegen/discovery/SchemaTest.java +++ b/src/test/java/com/google/api/codegen/discovery/SchemaTest.java @@ -34,7 +34,7 @@ public void testSchemaFromJson() throws IOException { ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(reader); - Schema schema = Schema.from(new DiscoveryNode(root), "root"); + Schema schema = Schema.from(new DiscoveryNode(root), null); Truth.assertThat(schema.description()).isEqualTo("My Foo."); Truth.assertThat(schema.id()).isEqualTo("Foo"); @@ -95,16 +95,18 @@ public void testSchemaFromJson() throws IOException { Truth.assertThat(properties.get("uint64").format()).isEqualTo(Schema.Format.UINT64); // Test path. - Truth.assertThat(schema.path()).isEqualTo("root"); - Truth.assertThat(schema.properties().get("array").path()).isEqualTo("root.properties.array"); - Truth.assertThat(schema.properties().get("array").items().path()) - .isEqualTo("root.properties.array.items"); - Truth.assertThat(schema.properties().get("object").additionalProperties().path()) - .isEqualTo("root.properties.object.additionalProperties"); + Truth.assertThat(schema.id()).isEqualTo("Foo"); + Truth.assertThat(schema.properties().get("array").parent()).isEqualTo(schema); + Truth.assertThat(schema.properties().get("array").items().parent()) + .isEqualTo(schema.properties().get("array")); + + Truth.assertThat(schema.properties().get("object").additionalProperties().parent()) + .isEqualTo(schema.properties().get("object")); } @Test public void testSchemaFromEmptyNode() { - Truth.assertThat(Schema.from(new DiscoveryNode(null), "").type()).isEqualTo(Schema.Type.EMPTY); + Truth.assertThat(Schema.from(new DiscoveryNode(null), null).type()) + .isEqualTo(Schema.Type.EMPTY); } } diff --git a/src/test/java/com/google/api/codegen/discoverytestdata/document_.json b/src/test/java/com/google/api/codegen/discoverytestdata/document_.json index 8abbfd38cb..293367a7e6 100644 --- a/src/test/java/com/google/api/codegen/discoverytestdata/document_.json +++ b/src/test/java/com/google/api/codegen/discoverytestdata/document_.json @@ -1,4 +1,5 @@ { + "id": "discovery#document", "auth": { "oauth2": { "scopes": {