Skip to content

Commit

Permalink
LLC Paging (#1061)
Browse files Browse the repository at this point in the history
* [Automation] Report

* [Automation] Report

* [Automation] Report

* Paging

* PagingAsync

* PagingAsync method

* ProtocolSyncPagingMethodTemplate

* PagingAsync method done

* ProtocolAsyncPagingSinglePageMethodTemplate

* log

* Filter group parameters in low level client methods, from #1062

* mapper, return type, paging method template

* Add imports for protocol paging

* PagingHelperMethods

* Fix a small issue

* client, wrapper methods

* Paging test

* module-info.java

* Remove stale code

* style

* Helper methods for paging

* Upgrade to Junit 5

* Add rawNextLinkName

* Add rawItemName; test

* test

* fix

* Robust code

Co-authored-by: Github Actions <[email protected]>
  • Loading branch information
qwordy and actions-user authored Jul 1, 2021
1 parent c539a85 commit 6c15cd0
Show file tree
Hide file tree
Showing 20 changed files with 5,596 additions and 209 deletions.
87 changes: 52 additions & 35 deletions fluent-tests/report.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,15 @@ public List<ClientMethod> map(Operation operation) {
}
IType listType = itemPropertyOpt.get().getWireType();
IType elementType = ((ListType) listType).getElementType();
asyncRestResponseReturnType = createPagedRestResponseReturnType(elementType);
asyncReturnType = createPagedAsyncReturnType(elementType);
syncReturnType = createPagedSyncReturnType(elementType);
if (settings.isLowLevelClient()) {
asyncRestResponseReturnType = createProtocolPagedRestResponseReturnType();
asyncReturnType = createProtocolPagedAsyncReturnType();
syncReturnType = createProtocolPagedSyncReturnType();
} else {
asyncRestResponseReturnType = createPagedRestResponseReturnType(elementType);
asyncReturnType = createPagedAsyncReturnType(elementType);
syncReturnType = createPagedSyncReturnType(elementType);
}
} else {
asyncRestResponseReturnType = null;
IType responseBodyType = SchemaUtil.getOperationResponseType(operation);
Expand Down Expand Up @@ -128,6 +134,9 @@ public List<ClientMethod> map(Operation operation) {
if (settings.isLowLevelClient()) {
codeModelParameters = request.getParameters().stream().filter(p ->
p.isRequired() && !(p.getSchema().getType() == Schema.AllSchemaTypes.GROUP)).collect(Collectors.toList());
// Only path param is allowed
// codeModelParameters = request.getParameters().stream().filter(
// p -> p.getProtocol().getHttp().getIn() == RequestParameterLocation.Path).collect(Collectors.toList());
} else {
codeModelParameters = request.getParameters().stream().filter(p -> !p.isFlattened()).collect(Collectors.toList());
}
Expand Down Expand Up @@ -247,7 +256,9 @@ && shouldGeneratePagingMethods()) {
CodeNamer.getPropertyName(operation.getExtensions().getXmsPageable().getNextLinkName()),
pageableItemName,
(nextMethods == null) ? null : nextMethods.stream().findFirst().get(),
lroIntermediateType);
lroIntermediateType,
operation.getExtensions().getXmsPageable().getNextLinkName(),
operation.getExtensions().getXmsPageable().getItemName());
builder.methodPageDetails(details);

if (!(!settings.getRequiredParameterClientMethods() && settings.isContextClientMethodParameter()
Expand Down Expand Up @@ -295,7 +306,9 @@ && shouldGeneratePagingMethods()) {
nextMethods.stream()
.filter(m -> m.getType() == ClientMethodType.PagingAsyncSinglePage)
.filter(m -> m.getMethodParameters().stream().anyMatch(p -> getContextType().equals(p.getClientType()))).findFirst().get(),
lroIntermediateType);
lroIntermediateType,
operation.getExtensions().getXmsPageable().getNextLinkName(),
operation.getExtensions().getXmsPageable().getItemName());
}

addClientMethodWithContext(methods,
Expand Down Expand Up @@ -621,6 +634,18 @@ protected IType createPagedRestResponseReturnType(IType elementType) {
return GenericType.Mono(GenericType.PagedResponse(elementType));
}

protected IType createProtocolPagedSyncReturnType() {
return GenericType.PagedIterable(ClassType.BinaryData);
}

protected IType createProtocolPagedAsyncReturnType() {
return GenericType.PagedFlux(ClassType.BinaryData);
}

protected IType createProtocolPagedRestResponseReturnType() {
return GenericType.Mono(GenericType.PagedResponse(ClassType.BinaryData));
}

protected ClientMethod.Builder getClientMethodBuilder() {
return new ClientMethod.Builder();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,17 @@ public void addImportsTo(Set<String> imports, boolean includeImplementationImpor
if (settings.isContextClientMethodParameter()) {
imports.add("com.azure.core.util.Context");
}

// Paging
if (getMethodPageDetails() != null) {
imports.add("com.azure.core.http.rest.PagedResponseBase");
imports.add("com.azure.core.http.rest.PagedResponse");
imports.add("com.azure.core.http.rest.PagedFlux");
imports.add("com.azure.core.http.rest.PagedIterable");
imports.add("java.util.List");
imports.add("java.util.Map");
imports.add("java.util.stream.Collectors");
}
} else {
getReturnValue().addImportsTo(imports, includeImplementationImports);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,47 @@ public class MethodPageDetails {
* Get whether or not this method is a request to get the next page of a sequence of pages.
*/
private String nextLinkName;
/**
* Raw nextLink name. It is the name in swagger and in response.
*/
private String rawNextLinkName;
private String itemName;
/**
* Raw item name. It is the name in swagger and in response.
*/
private String rawItemName;
private ClientMethod nextMethod;

// Proxy method return type is Flux<ByteBuffer>. Client method return type is PagedResponse<>.
// This intermediate type is the type of pagination response (the type with values and nextLink).
private IType lroIntermediateType;

public MethodPageDetails(String nextLinkName, String itemName, ClientMethod nextMethod, IType lroIntermediateType) {
public MethodPageDetails(String nextLinkName, String itemName, ClientMethod nextMethod, IType lroIntermediateType,
String rawNextLinkName, String rawItemName) {
this.nextLinkName = nextLinkName;
this.itemName = itemName;
this.nextMethod = nextMethod;
this.lroIntermediateType = lroIntermediateType;
this.rawNextLinkName = rawNextLinkName;
this.rawItemName = rawItemName;
}

public String getNextLinkName() {
return nextLinkName;
}

public String getRawNextLinkName() {
return rawNextLinkName;
}

public String getItemName() {
return itemName;
}

public String getRawItemName() {
return rawItemName;
}

public ClientMethod getNextMethod() {
return nextMethod;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,20 +335,23 @@ public final void write(ClientMethod clientMethod, JavaType typeBlock) {

switch (clientMethod.getType()) {
case PagingSync:
if (!settings.isLowLevelClient()) {
// TODO: https://github.com/Azure/autorest.java/issues/1034
if (settings.isLowLevelClient()) {
generateProtocolPagingSync(clientMethod, typeBlock, restAPIMethod, settings);
} else {
generatePagingSync(clientMethod, typeBlock, restAPIMethod, settings);
}
break;
case PagingAsync:
if (!settings.isLowLevelClient()) {
// TODO: https://github.com/Azure/autorest.java/issues/1034
if (settings.isLowLevelClient()) {
generateProtocolPagingAsync(clientMethod, typeBlock, restAPIMethod, settings);
} else {
generatePagingAsync(clientMethod, typeBlock, restAPIMethod, settings);
}
break;
case PagingAsyncSinglePage:
if (!settings.isLowLevelClient()) {
// TODO: https://github.com/Azure/autorest.java/issues/1034
if (settings.isLowLevelClient()) {
generateProtocolPagingAsyncSinglePage(clientMethod, typeBlock, restAPIMethod, settings);
} else {
generatePagedAsyncSinglePage(clientMethod, typeBlock, restAPIMethod, settings);
}
break;
Expand Down Expand Up @@ -437,6 +440,18 @@ public final void write(ClientMethod clientMethod, JavaType typeBlock) {
}
}

protected void generateProtocolPagingSync(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
generatePagingSync(clientMethod, typeBlock, restAPIMethod, settings);
}

protected void generateProtocolPagingAsync(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
generatePagingAsync(clientMethod, typeBlock, restAPIMethod, settings);
}

protected void generateProtocolPagingAsyncSinglePage(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
generatePagedAsyncSinglePage(clientMethod, typeBlock, restAPIMethod, settings);
}

protected void generatePagingSync(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
typeBlock.annotation("ServiceMethod(returns = ReturnType.COLLECTION)");
typeBlock.publicMethod(clientMethod.getDeclaration(), function -> {
Expand Down Expand Up @@ -762,79 +777,53 @@ private static String parameterDescriptionOrDefault(ProxyMethodParameter paramet
protected void generatePagedAsyncSinglePage(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
typeBlock.annotation("ServiceMethod(returns = ReturnType.SINGLE)");

if (clientMethod.getMethodPageDetails().nonNullNextLink()) {
writeMethod(typeBlock, clientMethod.getMethodVisibility(), clientMethod.getDeclaration(), function -> {
AddValidations(function, clientMethod.getRequiredNullableParameterExpressions(), clientMethod.getValidateExpressions(), settings);
AddOptionalAndConstantVariables(function, clientMethod, restAPIMethod.getParameters(), settings);
ApplyParameterTransformations(function, clientMethod, settings);
ConvertClientTypesToWireTypes(function, clientMethod, restAPIMethod.getParameters(), clientMethod.getClientReference(), settings);

String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, settings);
if (settings.getAddContextParameter()) {
if (settings.isContextClientMethodParameter() && contextInParameters(clientMethod)) {
function.line(String.format("return %s", serviceMethodCall));
} else {
function.line(String.format("return FluxUtil.withContext(context -> %s)",
serviceMethodCall));
}
writeMethod(typeBlock, clientMethod.getMethodVisibility(), clientMethod.getDeclaration(), function -> {
AddValidations(function, clientMethod.getRequiredNullableParameterExpressions(), clientMethod.getValidateExpressions(), settings);
AddOptionalAndConstantVariables(function, clientMethod, restAPIMethod.getParameters(), settings);
ApplyParameterTransformations(function, clientMethod, settings);
ConvertClientTypesToWireTypes(function, clientMethod, restAPIMethod.getParameters(), clientMethod.getClientReference(), settings);

String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, settings);
if (settings.getAddContextParameter()) {
if (settings.isContextClientMethodParameter() && contextInParameters(clientMethod)) {
function.line(String.format("return %s", serviceMethodCall));
} else {
function.line(String.format("return %s",
serviceMethodCall));
function.line(String.format("return FluxUtil.withContext(context -> %s)",
serviceMethodCall));
}
} else {
function.line(String.format("return %s",
serviceMethodCall));
}
function.indent(() -> {
function.line(".map(res -> new PagedResponseBase<>(");
function.indent(() -> {
function.line(".map(res -> new PagedResponseBase<>(");
function.indent(() -> {
function.line("res.getRequest(),");
function.line("res.getStatusCode(),");
function.line("res.getHeaders(),");
function.line("res.getRequest(),");
function.line("res.getStatusCode(),");
function.line("res.getHeaders(),");
if (settings.isLowLevelClient()) {
function.line("getValues(res.getValue(), \"%s\"),", clientMethod.getMethodPageDetails().getRawItemName());
} else {
function.line("res.getValue().%s(),", CodeNamer.getModelNamer().modelPropertyGetterName(clientMethod.getMethodPageDetails().getItemName()));
function.line("res.getValue().%s(),", CodeNamer.getModelNamer().modelPropertyGetterName(clientMethod.getMethodPageDetails().getNextLinkName()));
IType responseType = ((GenericType) clientMethod.getProxyMethod().getReturnType()).getTypeArguments()[0];
if (responseType instanceof ClassType) {
function.line("res.getDeserializedHeaders()));");
}
if (clientMethod.getMethodPageDetails().nonNullNextLink()) {
if (settings.isLowLevelClient()) {
function.line("getNextLink(res.getValue(), \"%s\"),", clientMethod.getMethodPageDetails().getRawNextLinkName());
} else {
function.line("null));");
function.line("res.getValue().%s(),", CodeNamer.getModelNamer().modelPropertyGetterName(clientMethod.getMethodPageDetails().getNextLinkName()));
}
});
});
});
} else {
writeMethod(typeBlock, clientMethod.getMethodVisibility(), clientMethod.getDeclaration(), function -> {
AddValidations(function, clientMethod.getRequiredNullableParameterExpressions(), clientMethod.getValidateExpressions(), settings);
AddOptionalAndConstantVariables(function, clientMethod, restAPIMethod.getParameters(), settings);
ApplyParameterTransformations(function, clientMethod, settings);
ConvertClientTypesToWireTypes(function, clientMethod, restAPIMethod.getParameters(), clientMethod.getClientReference(), settings);

String serviceMethodCall = checkAndReplaceParamNameCollision(clientMethod, restAPIMethod, settings);
if (settings.getAddContextParameter()) {
if (settings.isContextClientMethodParameter() && contextInParameters(clientMethod)) {
function.line(String.format("return %s", serviceMethodCall));
} else {
function.line(String.format("return FluxUtil.withContext(context -> %s)",
serviceMethodCall));
}
} else {
function.line(String.format("return %s",
serviceMethodCall));
}
function.indent(() -> {
function.line(".map(res -> new PagedResponseBase<>(");
function.indent(() -> {
function.line("res.getRequest(),");
function.line("res.getStatusCode(),");
function.line("res.getHeaders(),");
function.line("res.getValue().%s(),", CodeNamer.getModelNamer().modelPropertyGetterName(clientMethod.getMethodPageDetails().getItemName()));
function.line("null,");
IType responseType = ((GenericType) clientMethod.getProxyMethod().getReturnType()).getTypeArguments()[0];
if (responseType instanceof ClassType) {
function.line("res.getDeserializedHeaders()));");
} else {
function.line("null));");
}
});
}
IType responseType = ((GenericType) clientMethod.getProxyMethod().getReturnType()).getTypeArguments()[0];
if (responseType instanceof ClassType) {
function.line("res.getDeserializedHeaders()));");
} else {
function.line("null));");
}
});
});
}
});
}

private String checkAndReplaceParamNameCollision(ClientMethod clientMethod, ProxyMethod restAPIMethod, JavaSettings settings) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@
import com.azure.autorest.extension.base.plugin.JavaSettings;
import com.azure.autorest.model.clientmodel.IType;
import com.azure.autorest.model.clientmodel.MethodGroupClient;
import com.azure.autorest.model.javamodel.JavaBlock;
import com.azure.autorest.model.javamodel.JavaClass;
import com.azure.autorest.model.javamodel.JavaFile;
import com.azure.autorest.model.javamodel.JavaVisibility;
import com.azure.autorest.model.javamodel.*;
import com.azure.autorest.util.ClientModelUtil;
import com.azure.core.util.BinaryData;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -93,10 +89,28 @@ public final void write(MethodGroupClient methodGroupClient, JavaFile javaFile)
Templates.getClientMethodTemplate().write(clientMethod, classBlock);
}

writePagingHelperMethods(methodGroupClient, classBlock);

writeAdditionalClassBlock(classBlock);
});
}

protected void writePagingHelperMethods(MethodGroupClient methodGroupClient, JavaClass classBlock) {
classBlock.privateMethod("List<BinaryData> getValues(BinaryData binaryData, String path)", block -> {
block.line("try {");
block.line("Object obj = binaryData.toObject(Object.class);");
block.line("Object values = ((Map)obj).get(path);");
block.line("return (List<BinaryData>)(((List)values).stream().map(BinaryData::fromObject).collect(Collectors.toList()));");
block.line("} catch (Exception e) { return null; }");
});
classBlock.privateMethod("String getNextLink(BinaryData binaryData, String path)", block -> {
block.line("try {");
block.line("Object obj = binaryData.toObject(Object.class);");
block.line("return (String)((Map)obj).getOrDefault(path, null);");
block.line("} catch (Exception e) { return null; }");
});
}

protected void writeAdditionalClassBlock(JavaClass classBlock) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ public interface TemplateFactory {

ModuleInfoTemplate getModuleInfoTemplate();
}

Loading

0 comments on commit 6c15cd0

Please sign in to comment.