diff --git a/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java index c99ac7b248..8333a37f64 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java @@ -333,15 +333,21 @@ private MethodDefinition createRpcTestMethod( String mockServiceVarName = getMockServiceVarName(rpcService); if (method.isPaged()) { Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( "No repeated field found for paged method %s with output message type %s", method.name(), methodOutputMessage.name())); - // Must be a non-repeated type. - repeatedResponseType = repeatedPagedResultsField.type(); + if (repeatedPagedResultsField.isMap()) { + repeatedResponseType = + TypeNode.withReference(repeatedPagedResultsField.type().reference().generics().get(1)); + } else { + // Must be a non-repeated type. + repeatedResponseType = repeatedPagedResultsField.type(); + } + responsesElementVarExpr = VariableExpr.withVariable( Variable.builder().setType(repeatedResponseType).setName("responsesElement").build()); @@ -364,7 +370,7 @@ private MethodDefinition createRpcTestMethod( Expr expectedResponseValExpr = null; if (method.isPaged()) { Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field firstRepeatedField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field firstRepeatedField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( firstRepeatedField, String.format( @@ -373,7 +379,10 @@ private MethodDefinition createRpcTestMethod( expectedResponseValExpr = DefaultValueComposer.createSimplePagedResponse( - method.outputType(), firstRepeatedField.name(), responsesElementVarExpr); + method.outputType(), + firstRepeatedField.name(), + responsesElementVarExpr, + firstRepeatedField.isMap()); } else { if (messageTypes.containsKey(methodOutputType.reference().fullName())) { expectedResponseValExpr = @@ -516,6 +525,9 @@ private MethodDefinition createRpcTestMethod( } if (method.isPaged()) { + Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); + // Assign the resources variable. VariableExpr resourcesVarExpr = VariableExpr.withVariable( @@ -524,7 +536,7 @@ private MethodDefinition createRpcTestMethod( TypeNode.withReference( ConcreteReference.builder() .setClazz(List.class) - .setGenerics(Arrays.asList(repeatedResponseType.reference())) + .setGenerics(Arrays.asList(repeatedPagedResultsField.type().reference())) .build())) .setName("resources") .build()); @@ -570,8 +582,6 @@ private MethodDefinition createRpcTestMethod( .build()); // Assert the responses are equivalent. - Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( @@ -580,19 +590,52 @@ private MethodDefinition createRpcTestMethod( Expr zeroExpr = ValueExpr.withValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("0").build()); - Expr expectedPagedResponseExpr = - MethodInvocationExpr.builder() - .setExprReferenceExpr(expectedResponseVarExpr) - .setMethodName( - String.format( - "get%sList", JavaStyle.toUpperCamelCase(repeatedPagedResultsField.name()))) - .build(); - expectedPagedResponseExpr = - MethodInvocationExpr.builder() - .setExprReferenceExpr(expectedPagedResponseExpr) - .setMethodName("get") - .setArguments(zeroExpr) - .build(); + + // Generated code: + // Assert.assertEquals( + // expectedResponse.getItemsMap().entrySet().iterator().next(), resources.get(0)); + // ) + Expr expectedPagedResponseExpr; + if (repeatedPagedResultsField.isMap()) { + expectedPagedResponseExpr = + MethodInvocationExpr.builder() + .setMethodName("next") + .setExprReferenceExpr( + MethodInvocationExpr.builder() + .setMethodName("iterator") + .setExprReferenceExpr( + MethodInvocationExpr.builder() + .setMethodName("entrySet") + .setExprReferenceExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(expectedResponseVarExpr) + .setMethodName( + String.format( + "get%sMap", + JavaStyle.toUpperCamelCase( + repeatedPagedResultsField.name()))) + .build()) + .build()) + .build()) + .build(); + + } else { + // Generated code: + // Assert.assertEquals(expectedResponse.getItemsList().get(0), resources.get(0)); + expectedPagedResponseExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(expectedResponseVarExpr) + .setMethodName( + String.format( + "get%sList", JavaStyle.toUpperCamelCase(repeatedPagedResultsField.name()))) + .build(); + expectedPagedResponseExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(expectedPagedResponseExpr) + .setMethodName("get") + .setArguments(zeroExpr) + .build(); + } Expr actualPagedResponseExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(resourcesVarExpr) diff --git a/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java index f8452a6f70..2d1163b5c4 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubSettingsClassComposer.java @@ -306,8 +306,7 @@ private static List createClassStatements( // Assign DEFAULT_SERVICE_SCOPES. statements.add(SettingsCommentComposer.DEFAULT_SCOPES_COMMENT); VariableExpr defaultServiceScopesDeclVarExpr = - DEFAULT_SERVICE_SCOPES_VAR_EXPR - .toBuilder() + DEFAULT_SERVICE_SCOPES_VAR_EXPR.toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) @@ -403,7 +402,7 @@ private static List createPagingStaticAssignExprs( "No method found for message type %s for method %s among %s", pagedResponseMessageKey, method.name(), messageTypes.keySet())); - Field repeatedPagedResultsField = pagedResponseMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = pagedResponseMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( @@ -512,7 +511,7 @@ private static Expr createPagedListDescriptorAssignExpr( returnExpr = MethodInvocationExpr.builder() .setExprReferenceExpr(newBuilderExpr) - .setMethodName("setPageSize") + .setMethodName("set" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setArguments(pageSizeVarExpr) .build(); returnExpr = @@ -543,7 +542,7 @@ private static Expr createPagedListDescriptorAssignExpr( .setReturnExpr( MethodInvocationExpr.builder() .setExprReferenceExpr(payloadVarExpr) - .setMethodName("getPageSize") + .setMethodName("get" + JavaStyle.toUpperCamelCase(method.pageSizeFieldName())) .setReturnType(returnType) .build()) .build()); @@ -573,14 +572,56 @@ private static Expr createPagedListDescriptorAssignExpr( .setClazz(Iterable.class) .setGenerics(Arrays.asList(repeatedResponseType.reference())) .build()); - Expr getResponsesListExpr = - MethodInvocationExpr.builder() - .setExprReferenceExpr(payloadVarExpr) - .setMethodName( - String.format("get%sList", JavaStyle.toUpperCamelCase(repeatedFieldName))) - .setReturnType(returnType) - .build(); + Expr getResponsesExpr; + Expr elseExpr; + Expr thenExpr; + if (repeatedResponseType.reference() != null + && "java.util.Map.Entry".equals(repeatedResponseType.reference().fullName())) { + getResponsesExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName( + String.format("get%sMap", JavaStyle.toUpperCamelCase(repeatedFieldName))) + .setReturnType(returnType) + .build(); + thenExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(Collections.class))) + .setGenerics(Arrays.asList(repeatedResponseType.reference())) + .setMethodName("emptySet") + .setReturnType(returnType) + .build(); + elseExpr = + MethodInvocationExpr.builder() + .setMethodName("entrySet") + .setExprReferenceExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName( + String.format("get%sMap", JavaStyle.toUpperCamelCase(repeatedFieldName))) + .build()) + .setReturnType(returnType) + .build(); + } else { + getResponsesExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName( + String.format("get%sList", JavaStyle.toUpperCamelCase(repeatedFieldName))) + .setReturnType(returnType) + .build(); + thenExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(ImmutableList.class))) + .setGenerics(Arrays.asList(repeatedResponseType.reference())) + .setMethodName("of") + .setReturnType(returnType) + .build(); + elseExpr = getResponsesExpr; + } // While protobufs should not be null, this null-check is needed to protect against NPEs // in paged iteration on clients that use legacy HTTP/JSON types, as these clients can // actually return null instead of an empty list. @@ -589,21 +630,13 @@ private static Expr createPagedListDescriptorAssignExpr( // Relevant discussion where this check was first added: // https://github.com/googleapis/google-cloud-java/pull/4499#discussion_r257057409 Expr conditionExpr = - RelationalOperationExpr.equalToWithExprs(getResponsesListExpr, ValueExpr.createNullExpr()); - Expr thenExpr = - MethodInvocationExpr.builder() - .setStaticReferenceType( - TypeNode.withReference(ConcreteReference.withClazz(ImmutableList.class))) - .setGenerics(Arrays.asList(repeatedResponseType.reference())) - .setMethodName("of") - .setReturnType(returnType) - .build(); + RelationalOperationExpr.equalToWithExprs(getResponsesExpr, ValueExpr.createNullExpr()); returnExpr = TernaryExpr.builder() .setConditionExpr(conditionExpr) .setThenExpr(thenExpr) - .setElseExpr(getResponsesListExpr) + .setElseExpr(elseExpr) .build(); anonClassMethods.add( methodStarterBuilder @@ -623,8 +656,7 @@ private static Expr createPagedListDescriptorAssignExpr( // Declare and assign the variable. return AssignmentExpr.builder() .setVariableExpr( - pagedListDescVarExpr - .toBuilder() + pagedListDescVarExpr.toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) @@ -763,8 +795,7 @@ private static Expr createPagedListResponseFactoryAssignExpr( return AssignmentExpr.builder() .setVariableExpr( - pagedListResponseFactoryVarExpr - .toBuilder() + pagedListResponseFactoryVarExpr.toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) @@ -818,8 +849,7 @@ private MethodDefinition createCreateStubMethod(Service service, TypeStore typeS // Set up the if-statement. Expr tRansportNameExpr = MethodInvocationExpr.builder() - .setStaticReferenceType( - getTransportContext().transportChannelType()) + .setStaticReferenceType(getTransportContext().transportChannelType()) .setMethodName(getTransportContext().transportGetterName()) .build(); @@ -842,7 +872,8 @@ private MethodDefinition createCreateStubMethod(Service service, TypeStore typeS Expr createExpr = MethodInvocationExpr.builder() .setStaticReferenceType( - typeStore.get(getTransportContext().classNames().getTransportServiceStubClassName(service))) + typeStore.get( + getTransportContext().classNames().getTransportServiceStubClassName(service))) .setMethodName("create") .setArguments( ValueExpr.withValue( @@ -995,7 +1026,8 @@ private List createDefaultHelperAndGetterMethods( return javaMethods; } - private static List createBuilderHelperMethods(Service service, TypeStore typeStore) { + private static List createBuilderHelperMethods( + Service service, TypeStore typeStore) { List javaMethods = new ArrayList<>(); // Create the newBuilder() method. final TypeNode builderReturnType = typeStore.get(NESTED_BUILDER_CLASS_NAME); diff --git a/src/main/java/com/google/api/generator/gapic/composer/common/ServiceClientClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/common/ServiceClientClassComposer.java index 97cf85f5ad..d0afbca0e5 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/common/ServiceClientClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/common/ServiceClientClassComposer.java @@ -1042,7 +1042,7 @@ private static List createNestedPagingClasses( } // Find the repeated field. Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( diff --git a/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java b/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java index 707b46588e..75562d680c 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/defaultvalue/DefaultValueComposer.java @@ -43,6 +43,7 @@ import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -334,7 +335,19 @@ public static Expr createSimpleOperationBuilderExpr(String name, VariableExpr re } public static Expr createSimplePagedResponse( - TypeNode responseType, String repeatedFieldName, Expr responseElementVarExpr) { + TypeNode responseType, String repeatedFieldName, Expr responseElementVarExpr, boolean isMap) { + // Code for paginated maps: + // AggregatedMessageList.newBuilder() + // .setNextPageToken("") + // .putAllItems(Collections.singletonMap("items", responsesElement)) + // .build(); + // + // Code for paginated arrays: + // MessageList expectedResponse = + // AddressList.newBuilder() + // .setNextPageToken("") + // .addAllItems(Arrays.asList(responsesElement)) + // .build(); Expr pagedResponseExpr = MethodInvocationExpr.builder() .setStaticReferenceType(responseType) @@ -346,18 +359,39 @@ public static Expr createSimplePagedResponse( .setMethodName("setNextPageToken") .setArguments(ValueExpr.withValue(StringObjectValue.withValue(""))) .build(); - pagedResponseExpr = - MethodInvocationExpr.builder() - .setExprReferenceExpr(pagedResponseExpr) - .setMethodName(String.format("addAll%s", JavaStyle.toUpperCamelCase(repeatedFieldName))) - .setArguments( - MethodInvocationExpr.builder() - .setStaticReferenceType( - TypeNode.withReference(ConcreteReference.withClazz(Arrays.class))) - .setMethodName("asList") - .setArguments(responseElementVarExpr) - .build()) - .build(); + if (isMap) { + pagedResponseExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(pagedResponseExpr) + .setMethodName( + String.format("putAll%s", JavaStyle.toUpperCamelCase(repeatedFieldName))) + .setArguments( + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(Collections.class))) + .setMethodName("singletonMap") + .setArguments( + ValueExpr.withValue( + StringObjectValue.withValue( + JavaStyle.toLowerCamelCase(repeatedFieldName))), + responseElementVarExpr) + .build()) + .build(); + } else { + pagedResponseExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(pagedResponseExpr) + .setMethodName( + String.format("addAll%s", JavaStyle.toUpperCamelCase(repeatedFieldName))) + .setArguments( + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(Arrays.class))) + .setMethodName("asList") + .setArguments(responseElementVarExpr) + .build()) + .build(); + } return MethodInvocationExpr.builder() .setExprReferenceExpr(pagedResponseExpr) .setMethodName("build") diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java index a9403b54a3..0e1b337670 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposer.java @@ -477,7 +477,7 @@ public static String composePagedCallableMethodHeaderSampleCode( // Find the repeated field. Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( @@ -708,7 +708,7 @@ private static List composeUnaryPagedRpcMethodBodyStatements( "Output message %s not found, keys: ", method.outputType().reference().fullName(), messageTypes.keySet().toString()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( @@ -1134,7 +1134,7 @@ private static List composePagedCallableBodyStatements( Map messageTypes) { // Find the repeated field. Message methodOutputMessage = messageTypes.get(method.outputType().reference().fullName()); - Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapPaginatedRepeatedField(); Preconditions.checkNotNull( repeatedPagedResultsField, String.format( diff --git a/src/main/java/com/google/api/generator/gapic/model/Message.java b/src/main/java/com/google/api/generator/gapic/model/Message.java index ad1efa285a..6bfcbb86aa 100644 --- a/src/main/java/com/google/api/generator/gapic/model/Message.java +++ b/src/main/java/com/google/api/generator/gapic/model/Message.java @@ -14,11 +14,13 @@ package com.google.api.generator.gapic.model; +import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.TypeNode; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -71,7 +73,22 @@ public boolean hasResource() { /** Returns the first list repeated field in a message, unwrapped from its list type. */ @Nullable - public Field findAndUnwrapFirstRepeatedField() { + public Field findAndUnwrapPaginatedRepeatedField() { + for (Field field : fields()) { + if (field.isMap()) { + List repeatedGenericMapRefs = field.type().reference().generics(); + + TypeNode paginatedType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(Map.Entry.class) + .setGenerics( + Arrays.asList(repeatedGenericMapRefs.get(0), repeatedGenericMapRefs.get(1))) + .build()); + + return field.toBuilder().setType(paginatedType).build(); + } + } for (Field field : fields()) { if (field.isRepeated() && !field.isMap()) { Reference repeatedGenericRef = field.type().reference().generics().get(0); diff --git a/src/main/java/com/google/api/generator/gapic/model/Method.java b/src/main/java/com/google/api/generator/gapic/model/Method.java index 7fa72d7a1a..56a1403098 100644 --- a/src/main/java/com/google/api/generator/gapic/model/Method.java +++ b/src/main/java/com/google/api/generator/gapic/model/Method.java @@ -39,7 +39,12 @@ public enum Stream { public abstract boolean isBatching(); - public abstract boolean isPaged(); + public boolean isPaged() { + return pageSizeFieldName() != null; + } + + @Nullable + public abstract String pageSizeFieldName(); public abstract boolean isDeprecated(); @@ -84,7 +89,6 @@ public static Builder builder() { .setStream(Stream.NONE) .setMethodSignatures(ImmutableList.of()) .setIsBatching(false) - .setIsPaged(false) .setIsDeprecated(false); } @@ -123,7 +127,7 @@ public abstract static class Builder { public abstract Builder setIsBatching(boolean isBatching); - public abstract Builder setIsPaged(boolean isPaged); + public abstract Builder setPageSizeFieldName(String pagedFieldName); public abstract Builder setIsDeprecated(boolean isDeprecated); diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index 4505c7242f..051e56f873 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -144,6 +144,7 @@ public static GapicContext parse(CodeGeneratorRequest request) { Set outputArgResourceNames = new HashSet<>(); List mixinServices = new ArrayList<>(); + Transport transport = Transport.parse(transportOpt.orElse(Transport.GRPC.toString())); List services = parseServices( request, @@ -152,7 +153,8 @@ public static GapicContext parse(CodeGeneratorRequest request) { outputArgResourceNames, serviceYamlProtoOpt, serviceConfigOpt, - mixinServices); + mixinServices, + transport); Preconditions.checkState(!services.isEmpty(), "No services found to generate"); Function typeNameFn = @@ -191,7 +193,7 @@ public static GapicContext parse(CodeGeneratorRequest request) { .setServiceConfig(serviceConfigOpt.isPresent() ? serviceConfigOpt.get() : null) .setGapicMetadataEnabled(willGenerateMetadata) .setServiceYamlProto(serviceYamlProtoOpt.isPresent() ? serviceYamlProtoOpt.get() : null) - .setTransport(Transport.parse(transportOpt.orElse(Transport.GRPC.toString()))) + .setTransport(transport) .build(); } @@ -202,7 +204,8 @@ public static List parseServices( Set outputArgResourceNames, Optional serviceYamlProtoOpt, Optional serviceConfigOpt, - List outputMixinServices) { + List outputMixinServices, + Transport transport) { Map fileDescriptors = getFilesToGenerate(request); List services = new ArrayList<>(); @@ -220,7 +223,8 @@ public static List parseServices( resourceNames, serviceYamlProtoOpt, serviceConfigOpt, - outputArgResourceNames)); + outputArgResourceNames, + transport)); } // Prevent codegen for mixed-in services if there are other services present, since that is an @@ -379,7 +383,8 @@ public static List parseService( resourceNames, serviceYamlProtoOpt, Optional.empty(), - outputArgResourceNames); + outputArgResourceNames, + Transport.GRPC); } public static List parseService( @@ -388,7 +393,8 @@ public static List parseService( Map resourceNames, Optional serviceYamlProtoOpt, Optional serviceConfigOpt, - Set outputArgResourceNames) { + Set outputArgResourceNames, + Transport transport) { return fileDescriptor.getServices().stream() .map( @@ -462,7 +468,8 @@ public static List parseService( messageTypes, resourceNames, serviceConfigOpt, - outputArgResourceNames)) + outputArgResourceNames, + transport)) .build(); }) .collect(Collectors.toList()); @@ -610,7 +617,8 @@ static List parseMethods( Map messageTypes, Map resourceNames, Optional serviceConfigOpt, - Set outputArgResourceNames) { + Set outputArgResourceNames, + Transport transport) { List methods = new ArrayList<>(); for (MethodDescriptor protoMethod : serviceDescriptor.getMethods()) { // Parse the method. @@ -662,7 +670,7 @@ static List parseMethods( outputArgResourceNames)) .setHttpBindings(httpBindings) .setIsBatching(isBatching) - .setIsPaged(parseIsPaged(protoMethod, messageTypes)) + .setPageSizeFieldName(parsePageSizeFieldName(protoMethod, messageTypes, transport)) .setIsDeprecated(isDeprecated) .build()); @@ -777,8 +785,8 @@ static LongrunningOperation parseLro( } @VisibleForTesting - static boolean parseIsPaged( - MethodDescriptor methodDescriptor, Map messageTypes) { + static String parsePageSizeFieldName( + MethodDescriptor methodDescriptor, Map messageTypes, Transport transport) { TypeNode inputMessageType = TypeParser.parseType(methodDescriptor.getInputType()); TypeNode outputMessageType = TypeParser.parseType(methodDescriptor.getOutputType()); Message inputMessage = messageTypes.get(inputMessageType.reference().fullName()); @@ -787,9 +795,24 @@ static boolean parseIsPaged( // This should technically handle the absence of either of these fields (aip.dev/158), but we // gate on their collective presence to ensure the generated surface is backawrds-compatible // with monolith-gnerated libraries. - return inputMessage.fieldMap().containsKey("page_size") - && inputMessage.fieldMap().containsKey("page_token") - && outputMessage.fieldMap().containsKey("next_page_token"); + String pagedFieldName = null; + + if (inputMessage.fieldMap().containsKey("page_token") + && outputMessage.fieldMap().containsKey("next_page_token")) { + // List of potential field names representing page size. + // page_size gets priority over max_results if both are present + List fieldNames = new ArrayList<>(); + fieldNames.add("page_size"); + if (transport == Transport.REST) { + fieldNames.add("max_results"); + } + for (String fieldName : fieldNames) { + if (pagedFieldName == null && inputMessage.fieldMap().containsKey(fieldName)) { + pagedFieldName = fieldName; + } + } + } + return pagedFieldName; } @VisibleForTesting diff --git a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java index e360b1eb3a..4031df1c0a 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientSampleCodeComposerTest.java @@ -47,6 +47,7 @@ public class ServiceClientSampleCodeComposerTest { private static final String SHOWCASE_PACKAGE_NAME = "com.google.showcase.v1beta1"; private static final String LRO_PACKAGE_NAME = "com.google.longrunning"; private static final String PROTO_PACKAGE_NAME = "com.google.protobuf"; + private static final String PAGINATED_FIELD_NAME = "page_size"; // =============================== Class Header Sample Code ===============================// @Test @@ -1005,7 +1006,7 @@ public void validComposeRpcMethodHeaderSampleCode_pagedRpcWithMultipleMethodArgu .setMethodSignatures(Arrays.asList(arguments)) .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); Reference repeatedResponseReference = VaporReference.builder().setName("Content").setPakkage(SHOWCASE_PACKAGE_NAME).build(); @@ -1078,7 +1079,7 @@ public void validComposeRpcMethodHeaderSampleCode_pagedRpcWithNoMethodArguments( .setMethodSignatures(Arrays.asList(arguments)) .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); Reference repeatedResponseReference = VaporReference.builder().setName("Content").setPakkage(SHOWCASE_PACKAGE_NAME).build(); @@ -1148,7 +1149,7 @@ public void invalidComposeRpcMethodHeaderSampleCode_noMatchedRepeatedResponseTyp .setMethodSignatures(Arrays.asList(methodArguments)) .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); assertThrows( NullPointerException.class, @@ -1187,7 +1188,7 @@ public void invalidComposeRpcMethodHeaderSampleCode_noRepeatedResponseTypeInPage .setMethodSignatures(Arrays.asList(methodArguments)) .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); Field responseField = Field.builder() @@ -1439,7 +1440,7 @@ public void validComposeRpcDefaultMethodHeaderSampleCode_isPagedMethod() { .setInputType(inputType) .setOutputType(outputType) .setMethodSignatures(Collections.emptyList()) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); String results = ServiceClientSampleCodeComposer.composeRpcDefaultMethodHeaderSampleCode( @@ -1489,7 +1490,7 @@ public void invalidComposeRpcDefaultMethodHeaderSampleCode_isPagedMethod() { .setInputType(inputType) .setOutputType(outputType) .setMethodSignatures(Collections.emptyList()) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); assertThrows( NullPointerException.class, @@ -1835,7 +1836,7 @@ public void validComposePagedCallableMethodHeaderSampleCode() { .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); String results = ServiceClientSampleCodeComposer.composePagedCallableMethodHeaderSampleCode( @@ -1887,7 +1888,7 @@ public void invalidComposePagedCallableMethodHeaderSampleCode_inputTypeNotExistI .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); assertThrows( NullPointerException.class, @@ -1924,7 +1925,7 @@ public void invalidComposePagedCallableMethodHeaderSampleCode_noExistMethodRespo .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); assertThrows( NullPointerException.class, @@ -1961,7 +1962,7 @@ public void invalidComposePagedCallableMethodHeaderSampleCode_noRepeatedResponse .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); Field responseField = Field.builder().setName("response").setType(TypeNode.STRING).build(); Message noRepeatedResponseMessage = @@ -2449,7 +2450,7 @@ public void validComposeRegularCallableMethodHeaderSampleCode_pageRpc() { .setInputType(inputType) .setOutputType(outputType) .setMethodSignatures(Collections.emptyList()) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); String results = ServiceClientSampleCodeComposer.composeRegularCallableMethodHeaderSampleCode( @@ -2539,7 +2540,7 @@ public void invalidComposeRegularCallableMethodHeaderSampleCode_noExistMethodRes .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); assertThrows( NullPointerException.class, @@ -2576,7 +2577,7 @@ public void invalidComposeRegularCallableMethodHeaderSampleCode_noRepeatedRespon .setName("PagedExpand") .setInputType(inputType) .setOutputType(outputType) - .setIsPaged(true) + .setPageSizeFieldName(PAGINATED_FIELD_NAME) .build(); Field responseField = Field.builder().setName("response").setType(TypeNode.STRING).build(); Message noRepeatedResponseMessage = diff --git a/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java index da661e5d42..337f11c2eb 100644 --- a/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java +++ b/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java @@ -30,6 +30,7 @@ import com.google.api.generator.gapic.model.MethodArgument; import com.google.api.generator.gapic.model.ResourceName; import com.google.api.generator.gapic.model.ResourceReference; +import com.google.api.generator.gapic.model.Transport; import com.google.common.collect.ImmutableList; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.MethodDescriptor; @@ -108,7 +109,8 @@ public void parseMethods_basic() { messageTypes, resourceNames, Optional.empty(), - outputResourceNames); + outputResourceNames, + Transport.GRPC); assertEquals(10, methods.size()); @@ -165,7 +167,8 @@ public void parseMethods_basicLro() { messageTypes, resourceNames, Optional.empty(), - outputResourceNames); + outputResourceNames, + Transport.GRPC); assertEquals(10, methods.size()); @@ -216,7 +219,8 @@ public void parseMethodSignatures_empty() { messageTypes, resourceNames, Optional.empty(), - outputResourceNames); + outputResourceNames, + Transport.GRPC); assertThat( MethodSignatureParser.parseMethodSignatures( methodDescriptor, @@ -245,7 +249,8 @@ public void parseMethodSignatures_validArgstAndEmptyString() { messageTypes, resourceNames, Optional.empty(), - outputResourceNames); + outputResourceNames, + Transport.GRPC); List> methodArgs = MethodSignatureParser.parseMethodSignatures( methodDescriptor, diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClient.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClient.java index 8323bbbbcd..17aa70fee5 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClient.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClient.java @@ -16,12 +16,22 @@ package com.google.cloud.compute.v1; +import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.api.core.BetaApi; import com.google.api.gax.core.BackgroundResource; +import com.google.api.gax.paging.AbstractFixedSizeCollection; +import com.google.api.gax.paging.AbstractPage; +import com.google.api.gax.paging.AbstractPagedListResponse; +import com.google.api.gax.rpc.PageContext; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.compute.v1.stub.AddressesStub; import com.google.cloud.compute.v1.stub.AddressesStubSettings; +import com.google.common.util.concurrent.MoreExecutors; import java.io.IOException; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import javax.annotation.Generated; @@ -37,7 +47,9 @@ *
{@code
  * try (AddressesClient addressesClient = AddressesClient.create()) {
  *   String project = "project-309310695";
- *   AddressAggregatedList response = addressesClient.aggregatedList(project);
+ *   String region = "region-934795532";
+ *   String address = "address-1147692044";
+ *   Operation response = addressesClient.delete(project, region, address);
  * }
  * }
* @@ -147,14 +159,17 @@ public AddressesStub getStub() { *
{@code
    * try (AddressesClient addressesClient = AddressesClient.create()) {
    *   String project = "project-309310695";
-   *   AddressAggregatedList response = addressesClient.aggregatedList(project);
+   *   for (Map.Entry element :
+   *       addressesClient.aggregatedList(project).iterateAll()) {
+   *     // doThingsWith(element);
+   *   }
    * }
    * }
* * @param project Project ID for this request. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ - public final AddressAggregatedList aggregatedList(String project) { + public final AggregatedListPagedResponse aggregatedList(String project) { AggregatedListAddressesRequest request = AggregatedListAddressesRequest.newBuilder().setProject(project).build(); return aggregatedList(request); @@ -177,15 +192,18 @@ public final AddressAggregatedList aggregatedList(String project) { * .setPageToken("pageToken873572522") * .setProject("project-309310695") * .build(); - * AddressAggregatedList response = addressesClient.aggregatedList(request); + * for (Map.Entry element : + * addressesClient.aggregatedList(request).iterateAll()) { + * // doThingsWith(element); + * } * } * } * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ - public final AddressAggregatedList aggregatedList(AggregatedListAddressesRequest request) { - return aggregatedListCallable().call(request); + public final AggregatedListPagedResponse aggregatedList(AggregatedListAddressesRequest request) { + return aggregatedListPagedCallable().call(request); } // AUTO-GENERATED DOCUMENTATION AND METHOD. @@ -205,10 +223,49 @@ public final AddressAggregatedList aggregatedList(AggregatedListAddressesRequest * .setPageToken("pageToken873572522") * .setProject("project-309310695") * .build(); - * ApiFuture future = - * addressesClient.aggregatedListCallable().futureCall(request); + * ApiFuture> future = + * addressesClient.aggregatedListPagedCallable().futureCall(request); * // Do something. - * AddressAggregatedList response = future.get(); + * for (Map.Entry element : future.get().iterateAll()) { + * // doThingsWith(element); + * } + * } + * } + */ + public final UnaryCallable + aggregatedListPagedCallable() { + return stub.aggregatedListPagedCallable(); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Retrieves an aggregated list of addresses. + * + *

Sample code: + * + *

{@code
+   * try (AddressesClient addressesClient = AddressesClient.create()) {
+   *   AggregatedListAddressesRequest request =
+   *       AggregatedListAddressesRequest.newBuilder()
+   *           .setFilter("filter-1274492040")
+   *           .setIncludeAllScopes(true)
+   *           .setMaxResults(1128457243)
+   *           .setOrderBy("orderBy-1207110587")
+   *           .setPageToken("pageToken873572522")
+   *           .setProject("project-309310695")
+   *           .build();
+   *   while (true) {
+   *     AddressAggregatedList response = addressesClient.aggregatedListCallable().call(request);
+   *     for (Map.Entry element : response.getResponsesList()) {
+   *       // doThingsWith(element);
+   *     }
+   *     String nextPageToken = response.getNextPageToken();
+   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
+   *       request = request.toBuilder().setPageToken(nextPageToken).build();
+   *     } else {
+   *       break;
+   *     }
+   *   }
    * }
    * }
*/ @@ -390,7 +447,9 @@ public final UnaryCallable insertCallable() { * String project = "project-309310695"; * String region = "region-934795532"; * String orderBy = "orderBy-1207110587"; - * AddressList response = addressesClient.list(project, region, orderBy); + * for (Address element : addressesClient.list(project, region, orderBy).iterateAll()) { + * // doThingsWith(element); + * } * } * } * @@ -405,7 +464,7 @@ public final UnaryCallable insertCallable() { *

Currently, only sorting by name or creationTimestamp desc is supported. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ - public final AddressList list(String project, String region, String orderBy) { + public final ListPagedResponse list(String project, String region, String orderBy) { ListAddressesRequest request = ListAddressesRequest.newBuilder() .setProject(project) @@ -432,15 +491,17 @@ public final AddressList list(String project, String region, String orderBy) { * .setProject("project-309310695") * .setRegion("region-934795532") * .build(); - * AddressList response = addressesClient.list(request); + * for (Address element : addressesClient.list(request).iterateAll()) { + * // doThingsWith(element); + * } * } * } * * @param request The request object containing all of the parameters for the API call. * @throws com.google.api.gax.rpc.ApiException if the remote call fails */ - public final AddressList list(ListAddressesRequest request) { - return listCallable().call(request); + public final ListPagedResponse list(ListAddressesRequest request) { + return listPagedCallable().call(request); } // AUTO-GENERATED DOCUMENTATION AND METHOD. @@ -460,9 +521,47 @@ public final AddressList list(ListAddressesRequest request) { * .setProject("project-309310695") * .setRegion("region-934795532") * .build(); - * ApiFuture future = addressesClient.listCallable().futureCall(request); + * ApiFuture

future = addressesClient.listPagedCallable().futureCall(request); * // Do something. - * AddressList response = future.get(); + * for (Address element : future.get().iterateAll()) { + * // doThingsWith(element); + * } + * } + * } + */ + public final UnaryCallable listPagedCallable() { + return stub.listPagedCallable(); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Retrieves a list of addresses contained within the specified region. + * + *

Sample code: + * + *

{@code
+   * try (AddressesClient addressesClient = AddressesClient.create()) {
+   *   ListAddressesRequest request =
+   *       ListAddressesRequest.newBuilder()
+   *           .setFilter("filter-1274492040")
+   *           .setMaxResults(1128457243)
+   *           .setOrderBy("orderBy-1207110587")
+   *           .setPageToken("pageToken873572522")
+   *           .setProject("project-309310695")
+   *           .setRegion("region-934795532")
+   *           .build();
+   *   while (true) {
+   *     AddressList response = addressesClient.listCallable().call(request);
+   *     for (Address element : response.getResponsesList()) {
+   *       // doThingsWith(element);
+   *     }
+   *     String nextPageToken = response.getNextPageToken();
+   *     if (!Strings.isNullOrEmpty(nextPageToken)) {
+   *       request = request.toBuilder().setPageToken(nextPageToken).build();
+   *     } else {
+   *       break;
+   *     }
+   *   }
    * }
    * }
*/ @@ -499,4 +598,173 @@ public void shutdownNow() { public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException { return stub.awaitTermination(duration, unit); } + + public static class AggregatedListPagedResponse + extends AbstractPagedListResponse< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry, + AggregatedListPage, + AggregatedListFixedSizeCollection> { + + public static ApiFuture createAsync( + PageContext< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry> + context, + ApiFuture futureResponse) { + ApiFuture futurePage = + AggregatedListPage.createEmptyPage().createPageAsync(context, futureResponse); + return ApiFutures.transform( + futurePage, + new ApiFunction() { + @Override + public AggregatedListPagedResponse apply(AggregatedListPage input) { + return new AggregatedListPagedResponse(input); + } + }, + MoreExecutors.directExecutor()); + } + + private AggregatedListPagedResponse(AggregatedListPage page) { + super(page, AggregatedListFixedSizeCollection.createEmptyCollection()); + } + } + + public static class AggregatedListPage + extends AbstractPage< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry, + AggregatedListPage> { + + private AggregatedListPage( + PageContext< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry> + context, + AddressAggregatedList response) { + super(context, response); + } + + private static AggregatedListPage createEmptyPage() { + return new AggregatedListPage(null, null); + } + + @Override + protected AggregatedListPage createPage( + PageContext< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry> + context, + AddressAggregatedList response) { + return new AggregatedListPage(context, response); + } + + @Override + public ApiFuture createPageAsync( + PageContext< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry> + context, + ApiFuture futureResponse) { + return super.createPageAsync(context, futureResponse); + } + } + + public static class AggregatedListFixedSizeCollection + extends AbstractFixedSizeCollection< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry, + AggregatedListPage, + AggregatedListFixedSizeCollection> { + + private AggregatedListFixedSizeCollection(List pages, int collectionSize) { + super(pages, collectionSize); + } + + private static AggregatedListFixedSizeCollection createEmptyCollection() { + return new AggregatedListFixedSizeCollection(null, 0); + } + + @Override + protected AggregatedListFixedSizeCollection createCollection( + List pages, int collectionSize) { + return new AggregatedListFixedSizeCollection(pages, collectionSize); + } + } + + public static class ListPagedResponse + extends AbstractPagedListResponse< + ListAddressesRequest, AddressList, Address, ListPage, ListFixedSizeCollection> { + + public static ApiFuture createAsync( + PageContext context, + ApiFuture futureResponse) { + ApiFuture futurePage = + ListPage.createEmptyPage().createPageAsync(context, futureResponse); + return ApiFutures.transform( + futurePage, + new ApiFunction() { + @Override + public ListPagedResponse apply(ListPage input) { + return new ListPagedResponse(input); + } + }, + MoreExecutors.directExecutor()); + } + + private ListPagedResponse(ListPage page) { + super(page, ListFixedSizeCollection.createEmptyCollection()); + } + } + + public static class ListPage + extends AbstractPage { + + private ListPage( + PageContext context, AddressList response) { + super(context, response); + } + + private static ListPage createEmptyPage() { + return new ListPage(null, null); + } + + @Override + protected ListPage createPage( + PageContext context, AddressList response) { + return new ListPage(context, response); + } + + @Override + public ApiFuture createPageAsync( + PageContext context, + ApiFuture futureResponse) { + return super.createPageAsync(context, futureResponse); + } + } + + public static class ListFixedSizeCollection + extends AbstractFixedSizeCollection< + ListAddressesRequest, AddressList, Address, ListPage, ListFixedSizeCollection> { + + private ListFixedSizeCollection(List pages, int collectionSize) { + super(pages, collectionSize); + } + + private static ListFixedSizeCollection createEmptyCollection() { + return new ListFixedSizeCollection(null, 0); + } + + @Override + protected ListFixedSizeCollection createCollection(List pages, int collectionSize) { + return new ListFixedSizeCollection(pages, collectionSize); + } + } } diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClientTest.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClientTest.java index 6f66108448..bc7d9c2a16 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClientTest.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesClientTest.java @@ -16,6 +16,9 @@ package com.google.cloud.compute.v1; +import static com.google.cloud.compute.v1.AddressesClient.AggregatedListPagedResponse; +import static com.google.cloud.compute.v1.AddressesClient.ListPagedResponse; + import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.httpjson.GaxHttpJsonProperties; import com.google.api.gax.httpjson.testing.MockHttpService; @@ -26,10 +29,13 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.testing.FakeStatusCode; import com.google.cloud.compute.v1.stub.HttpJsonAddressesStub; +import com.google.common.collect.Lists; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import javax.annotation.Generated; import org.junit.After; import org.junit.AfterClass; @@ -74,21 +80,24 @@ public void tearDown() throws Exception { @Test public void aggregatedListTest() throws Exception { + AddressesScopedList responsesElement = AddressesScopedList.newBuilder().build(); AddressAggregatedList expectedResponse = AddressAggregatedList.newBuilder() - .setId("id3355") - .putAllItems(new HashMap()) - .setKind("kind3292052") - .setNextPageToken("nextPageToken-1386094857") - .setSelfLink("selfLink1191800166") - .setWarning(Warning.newBuilder().build()) + .setNextPageToken("") + .putAllItems(Collections.singletonMap("items", responsesElement)) .build(); mockService.addResponse(expectedResponse); String project = "project-309310695"; - AddressAggregatedList actualResponse = client.aggregatedList(project); - Assert.assertEquals(expectedResponse, actualResponse); + AggregatedListPagedResponse pagedListResponse = client.aggregatedList(project); + + List> resources = + Lists.newArrayList(pagedListResponse.iterateAll()); + + Assert.assertEquals(1, resources.size()); + Assert.assertEquals( + expectedResponse.getItemsMap().entrySet().iterator().next(), resources.get(0)); List actualRequests = mockService.getRequestPaths(); Assert.assertEquals(1, actualRequests.size()); @@ -261,14 +270,11 @@ public void insertExceptionTest() throws Exception { @Test public void listTest() throws Exception { + Address responsesElement = Address.newBuilder().build(); AddressList expectedResponse = AddressList.newBuilder() - .setId("id3355") - .addAllItems(new ArrayList
()) - .setKind("kind3292052") - .setNextPageToken("nextPageToken-1386094857") - .setSelfLink("selfLink1191800166") - .setWarning(Warning.newBuilder().build()) + .setNextPageToken("") + .addAllItems(Arrays.asList(responsesElement)) .build(); mockService.addResponse(expectedResponse); @@ -276,8 +282,12 @@ public void listTest() throws Exception { String region = "region-934795532"; String orderBy = "orderBy-1207110587"; - AddressList actualResponse = client.list(project, region, orderBy); - Assert.assertEquals(expectedResponse, actualResponse); + ListPagedResponse pagedListResponse = client.list(project, region, orderBy); + + List
resources = Lists.newArrayList(pagedListResponse.iterateAll()); + + Assert.assertEquals(1, resources.size()); + Assert.assertEquals(expectedResponse.getItemsList().get(0), resources.get(0)); List actualRequests = mockService.getRequestPaths(); Assert.assertEquals(1, actualRequests.size()); diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesSettings.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesSettings.java index e7ededa1b4..7e246c0a51 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesSettings.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/AddressesSettings.java @@ -16,6 +16,9 @@ package com.google.cloud.compute.v1; +import static com.google.cloud.compute.v1.AddressesClient.AggregatedListPagedResponse; +import static com.google.cloud.compute.v1.AddressesClient.ListPagedResponse; + import com.google.api.core.ApiFunction; import com.google.api.core.BetaApi; import com.google.api.gax.core.GoogleCredentialsProvider; @@ -24,6 +27,7 @@ import com.google.api.gax.rpc.ApiClientHeaderProvider; import com.google.api.gax.rpc.ClientContext; import com.google.api.gax.rpc.ClientSettings; +import com.google.api.gax.rpc.PagedCallSettings; import com.google.api.gax.rpc.StubSettings; import com.google.api.gax.rpc.TransportChannelProvider; import com.google.api.gax.rpc.UnaryCallSettings; @@ -47,15 +51,15 @@ *

The builder of this class is recursive, so contained classes are themselves builders. When * build() is called, the tree of builders is called to create the complete settings object. * - *

For example, to set the total timeout of aggregatedList to 30 seconds: + *

For example, to set the total timeout of delete to 30 seconds: * *

{@code
  * AddressesSettings.Builder addressesSettingsBuilder = AddressesSettings.newBuilder();
  * addressesSettingsBuilder
- *     .aggregatedListSettings()
+ *     .deleteSettings()
  *     .setRetrySettings(
  *         addressesSettingsBuilder
- *             .aggregatedListSettings()
+ *             .deleteSettings()
  *             .getRetrySettings()
  *             .toBuilder()
  *             .setTotalTimeout(Duration.ofSeconds(30))
@@ -67,7 +71,8 @@
 public class AddressesSettings extends ClientSettings {
 
   /** Returns the object with the settings used for calls to aggregatedList. */
-  public UnaryCallSettings
+  public PagedCallSettings<
+          AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse>
       aggregatedListSettings() {
     return ((AddressesStubSettings) getStubSettings()).aggregatedListSettings();
   }
@@ -83,7 +88,7 @@ public UnaryCallSettings insertSettings() {
   }
 
   /** Returns the object with the settings used for calls to list. */
-  public UnaryCallSettings listSettings() {
+  public PagedCallSettings listSettings() {
     return ((AddressesStubSettings) getStubSettings()).listSettings();
   }
 
@@ -186,7 +191,8 @@ public Builder applyToAllUnaryMethods(
     }
 
     /** Returns the builder for the settings used for calls to aggregatedList. */
-    public UnaryCallSettings.Builder
+    public PagedCallSettings.Builder<
+            AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse>
         aggregatedListSettings() {
       return getStubSettingsBuilder().aggregatedListSettings();
     }
@@ -202,7 +208,8 @@ public UnaryCallSettings.Builder insertSettings
     }
 
     /** Returns the builder for the settings used for calls to list. */
-    public UnaryCallSettings.Builder listSettings() {
+    public PagedCallSettings.Builder
+        listSettings() {
       return getStubSettingsBuilder().listSettings();
     }
 
diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/gapic_metadata.json b/test/integration/goldens/compute/com/google/cloud/compute/v1/gapic_metadata.json
index 3d8eb8ed66..14eb0320fd 100644
--- a/test/integration/goldens/compute/com/google/cloud/compute/v1/gapic_metadata.json
+++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/gapic_metadata.json
@@ -11,7 +11,7 @@
           "libraryClient": "AddressesClient",
           "rpcs": {
             "AggregatedList": {
-              "methods": ["aggregatedList", "aggregatedList", "aggregatedListCallable"]
+              "methods": ["aggregatedList", "aggregatedList", "aggregatedListPagedCallable", "aggregatedListCallable"]
             },
             "Delete": {
               "methods": ["delete", "delete", "deleteCallable"]
@@ -20,7 +20,7 @@
               "methods": ["insert", "insert", "insertCallable"]
             },
             "List": {
-              "methods": ["list", "list", "listCallable"]
+              "methods": ["list", "list", "listPagedCallable", "listCallable"]
             }
           }
         }
diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/package-info.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/package-info.java
index fbc46c7bdc..941f3ab751 100644
--- a/test/integration/goldens/compute/com/google/cloud/compute/v1/package-info.java
+++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/package-info.java
@@ -28,7 +28,9 @@
  * 
{@code
  * try (AddressesClient addressesClient = AddressesClient.create()) {
  *   String project = "project-309310695";
- *   AddressAggregatedList response = addressesClient.aggregatedList(project);
+ *   String region = "region-934795532";
+ *   String address = "address-1147692044";
+ *   Operation response = addressesClient.delete(project, region, address);
  * }
  * }
* diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStub.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStub.java index d192131a5b..b7d7f6e590 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStub.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStub.java @@ -16,6 +16,9 @@ package com.google.cloud.compute.v1.stub; +import static com.google.cloud.compute.v1.AddressesClient.AggregatedListPagedResponse; +import static com.google.cloud.compute.v1.AddressesClient.ListPagedResponse; + import com.google.api.gax.core.BackgroundResource; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.compute.v1.AddressAggregatedList; @@ -36,6 +39,11 @@ @Generated("by gapic-generator-java") public abstract class AddressesStub implements BackgroundResource { + public UnaryCallable + aggregatedListPagedCallable() { + throw new UnsupportedOperationException("Not implemented: aggregatedListPagedCallable()"); + } + public UnaryCallable aggregatedListCallable() { throw new UnsupportedOperationException("Not implemented: aggregatedListCallable()"); @@ -49,6 +57,10 @@ public UnaryCallable insertCallable() { throw new UnsupportedOperationException("Not implemented: insertCallable()"); } + public UnaryCallable listPagedCallable() { + throw new UnsupportedOperationException("Not implemented: listPagedCallable()"); + } + public UnaryCallable listCallable() { throw new UnsupportedOperationException("Not implemented: listCallable()"); } diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStubSettings.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStubSettings.java index d8268b878b..a1ccb48e46 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStubSettings.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/AddressesStubSettings.java @@ -16,7 +16,11 @@ package com.google.cloud.compute.v1.stub; +import static com.google.cloud.compute.v1.AddressesClient.AggregatedListPagedResponse; +import static com.google.cloud.compute.v1.AddressesClient.ListPagedResponse; + import com.google.api.core.ApiFunction; +import com.google.api.core.ApiFuture; import com.google.api.core.BetaApi; import com.google.api.gax.core.GaxProperties; import com.google.api.gax.core.GoogleCredentialsProvider; @@ -25,14 +29,22 @@ import com.google.api.gax.httpjson.HttpJsonTransportChannel; import com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider; import com.google.api.gax.retrying.RetrySettings; +import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiClientHeaderProvider; import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.rpc.PageContext; +import com.google.api.gax.rpc.PagedCallSettings; +import com.google.api.gax.rpc.PagedListDescriptor; +import com.google.api.gax.rpc.PagedListResponseFactory; import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StubSettings; import com.google.api.gax.rpc.TransportChannelProvider; import com.google.api.gax.rpc.UnaryCallSettings; +import com.google.api.gax.rpc.UnaryCallable; +import com.google.cloud.compute.v1.Address; import com.google.cloud.compute.v1.AddressAggregatedList; import com.google.cloud.compute.v1.AddressList; +import com.google.cloud.compute.v1.AddressesScopedList; import com.google.cloud.compute.v1.AggregatedListAddressesRequest; import com.google.cloud.compute.v1.DeleteAddressRequest; import com.google.cloud.compute.v1.InsertAddressRequest; @@ -43,7 +55,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Map; import javax.annotation.Generated; // AUTO-GENERATED DOCUMENTATION AND CLASS. @@ -61,15 +75,15 @@ *

The builder of this class is recursive, so contained classes are themselves builders. When * build() is called, the tree of builders is called to create the complete settings object. * - *

For example, to set the total timeout of aggregatedList to 30 seconds: + *

For example, to set the total timeout of delete to 30 seconds: * *

{@code
  * AddressesStubSettings.Builder addressesSettingsBuilder = AddressesStubSettings.newBuilder();
  * addressesSettingsBuilder
- *     .aggregatedListSettings()
+ *     .deleteSettings()
  *     .setRetrySettings(
  *         addressesSettingsBuilder
- *             .aggregatedListSettings()
+ *             .deleteSettings()
  *             .getRetrySettings()
  *             .toBuilder()
  *             .setTotalTimeout(Duration.ofSeconds(30))
@@ -86,14 +100,139 @@ public class AddressesStubSettings extends StubSettings {
           .add("https://www.googleapis.com/auth/cloud-platform")
           .build();
 
-  private final UnaryCallSettings
+  private final PagedCallSettings<
+          AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse>
       aggregatedListSettings;
   private final UnaryCallSettings deleteSettings;
   private final UnaryCallSettings insertSettings;
-  private final UnaryCallSettings listSettings;
+  private final PagedCallSettings
+      listSettings;
+
+  private static final PagedListDescriptor<
+          AggregatedListAddressesRequest,
+          AddressAggregatedList,
+          Map.Entry>
+      AGGREGATED_LIST_PAGE_STR_DESC =
+          new PagedListDescriptor<
+              AggregatedListAddressesRequest,
+              AddressAggregatedList,
+              Map.Entry>() {
+            @Override
+            public String emptyToken() {
+              return "";
+            }
+
+            @Override
+            public AggregatedListAddressesRequest injectToken(
+                AggregatedListAddressesRequest payload, String token) {
+              return AggregatedListAddressesRequest.newBuilder(payload).setPageToken(token).build();
+            }
+
+            @Override
+            public AggregatedListAddressesRequest injectPageSize(
+                AggregatedListAddressesRequest payload, int pageSize) {
+              return AggregatedListAddressesRequest.newBuilder(payload)
+                  .setMaxResults(pageSize)
+                  .build();
+            }
+
+            @Override
+            public Integer extractPageSize(AggregatedListAddressesRequest payload) {
+              return payload.getMaxResults();
+            }
+
+            @Override
+            public String extractNextToken(AddressAggregatedList payload) {
+              return payload.getNextPageToken();
+            }
+
+            @Override
+            public Iterable> extractResources(
+                AddressAggregatedList payload) {
+              return payload.getItemsMap() == null
+                  ? Collections.>emptySet()
+                  : payload.getItemsMap().entrySet();
+            }
+          };
+
+  private static final PagedListDescriptor
+      LIST_PAGE_STR_DESC =
+          new PagedListDescriptor() {
+            @Override
+            public String emptyToken() {
+              return "";
+            }
+
+            @Override
+            public ListAddressesRequest injectToken(ListAddressesRequest payload, String token) {
+              return ListAddressesRequest.newBuilder(payload).setPageToken(token).build();
+            }
+
+            @Override
+            public ListAddressesRequest injectPageSize(ListAddressesRequest payload, int pageSize) {
+              return ListAddressesRequest.newBuilder(payload).setMaxResults(pageSize).build();
+            }
+
+            @Override
+            public Integer extractPageSize(ListAddressesRequest payload) {
+              return payload.getMaxResults();
+            }
+
+            @Override
+            public String extractNextToken(AddressList payload) {
+              return payload.getNextPageToken();
+            }
+
+            @Override
+            public Iterable
extractResources(AddressList payload) { + return payload.getItemsList() == null + ? ImmutableList.
of() + : payload.getItemsList(); + } + }; + + private static final PagedListResponseFactory< + AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse> + AGGREGATED_LIST_PAGE_STR_FACT = + new PagedListResponseFactory< + AggregatedListAddressesRequest, + AddressAggregatedList, + AggregatedListPagedResponse>() { + @Override + public ApiFuture getFuturePagedResponse( + UnaryCallable callable, + AggregatedListAddressesRequest request, + ApiCallContext context, + ApiFuture futureResponse) { + PageContext< + AggregatedListAddressesRequest, + AddressAggregatedList, + Map.Entry> + pageContext = + PageContext.create(callable, AGGREGATED_LIST_PAGE_STR_DESC, request, context); + return AggregatedListPagedResponse.createAsync(pageContext, futureResponse); + } + }; + + private static final PagedListResponseFactory< + ListAddressesRequest, AddressList, ListPagedResponse> + LIST_PAGE_STR_FACT = + new PagedListResponseFactory() { + @Override + public ApiFuture getFuturePagedResponse( + UnaryCallable callable, + ListAddressesRequest request, + ApiCallContext context, + ApiFuture futureResponse) { + PageContext pageContext = + PageContext.create(callable, LIST_PAGE_STR_DESC, request, context); + return ListPagedResponse.createAsync(pageContext, futureResponse); + } + }; /** Returns the object with the settings used for calls to aggregatedList. */ - public UnaryCallSettings + public PagedCallSettings< + AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse> aggregatedListSettings() { return aggregatedListSettings; } @@ -109,7 +248,7 @@ public UnaryCallSettings insertSettings() { } /** Returns the object with the settings used for calls to list. */ - public UnaryCallSettings listSettings() { + public PagedCallSettings listSettings() { return listSettings; } @@ -196,11 +335,13 @@ protected AddressesStubSettings(Builder settingsBuilder) throws IOException { /** Builder for AddressesStubSettings. */ public static class Builder extends StubSettings.Builder { private final ImmutableList> unaryMethodSettingsBuilders; - private final UnaryCallSettings.Builder + private final PagedCallSettings.Builder< + AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse> aggregatedListSettings; private final UnaryCallSettings.Builder deleteSettings; private final UnaryCallSettings.Builder insertSettings; - private final UnaryCallSettings.Builder listSettings; + private final PagedCallSettings.Builder + listSettings; private static final ImmutableMap> RETRYABLE_CODE_DEFINITIONS; @@ -228,10 +369,10 @@ protected Builder() { protected Builder(ClientContext clientContext) { super(clientContext); - aggregatedListSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + aggregatedListSettings = PagedCallSettings.newBuilder(AGGREGATED_LIST_PAGE_STR_FACT); deleteSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); insertSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); - listSettings = UnaryCallSettings.newUnaryCallSettingsBuilder(); + listSettings = PagedCallSettings.newBuilder(LIST_PAGE_STR_FACT); unaryMethodSettingsBuilders = ImmutableList.>of( @@ -306,7 +447,8 @@ public Builder applyToAllUnaryMethods( } /** Returns the builder for the settings used for calls to aggregatedList. */ - public UnaryCallSettings.Builder + public PagedCallSettings.Builder< + AggregatedListAddressesRequest, AddressAggregatedList, AggregatedListPagedResponse> aggregatedListSettings() { return aggregatedListSettings; } @@ -322,7 +464,8 @@ public UnaryCallSettings.Builder insertSettings } /** Returns the builder for the settings used for calls to list. */ - public UnaryCallSettings.Builder listSettings() { + public PagedCallSettings.Builder + listSettings() { return listSettings; } diff --git a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/HttpJsonAddressesStub.java b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/HttpJsonAddressesStub.java index eeb7b31c69..74f92293c9 100644 --- a/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/HttpJsonAddressesStub.java +++ b/test/integration/goldens/compute/com/google/cloud/compute/v1/stub/HttpJsonAddressesStub.java @@ -16,6 +16,9 @@ package com.google.cloud.compute.v1.stub; +import static com.google.cloud.compute.v1.AddressesClient.AggregatedListPagedResponse; +import static com.google.cloud.compute.v1.AddressesClient.ListPagedResponse; + import com.google.api.client.http.HttpMethods; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; @@ -247,9 +250,12 @@ public String extract(ListAddressesRequest request) { private final UnaryCallable aggregatedListCallable; + private final UnaryCallable + aggregatedListPagedCallable; private final UnaryCallable deleteCallable; private final UnaryCallable insertCallable; private final UnaryCallable listCallable; + private final UnaryCallable listPagedCallable; private final BackgroundResource backgroundResources; private final HttpJsonStubCallableFactory callableFactory; @@ -312,6 +318,9 @@ protected HttpJsonAddressesStub( this.aggregatedListCallable = callableFactory.createUnaryCallable( aggregatedListTransportSettings, settings.aggregatedListSettings(), clientContext); + this.aggregatedListPagedCallable = + callableFactory.createPagedCallable( + aggregatedListTransportSettings, settings.aggregatedListSettings(), clientContext); this.deleteCallable = callableFactory.createUnaryCallable( deleteTransportSettings, settings.deleteSettings(), clientContext); @@ -321,6 +330,9 @@ protected HttpJsonAddressesStub( this.listCallable = callableFactory.createUnaryCallable( listTransportSettings, settings.listSettings(), clientContext); + this.listPagedCallable = + callableFactory.createPagedCallable( + listTransportSettings, settings.listSettings(), clientContext); this.backgroundResources = new BackgroundResourceAggregation(clientContext.getBackgroundResources()); @@ -342,6 +354,12 @@ public static List getMethodDescriptors() { return aggregatedListCallable; } + @Override + public UnaryCallable + aggregatedListPagedCallable() { + return aggregatedListPagedCallable; + } + @Override public UnaryCallable deleteCallable() { return deleteCallable; @@ -357,6 +375,11 @@ public UnaryCallable listCallable() { return listCallable; } + @Override + public UnaryCallable listPagedCallable() { + return listPagedCallable; + } + @Override public final void close() { shutdown();