Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support body string in protocol client #1062

Merged
merged 3 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions generate.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set VANILLA_ARGUMENTS=--version=3.1.3 --java --use=. --output-folder=vanilla-tests --sync-methods=all --client-side-validations --add-context-parameter --required-parameter-client-methods
set AZURE_ARGUMENTS=--version=3.1.3 --java --use=. --output-folder=azure-tests --sync-methods=all --client-side-validations --add-context-parameter --required-parameter-client-methods
set ARM_ARGUMENTS=--version=3.1.3 --java --use=. --output-folder=azure-tests --azure-arm --fluent=lite --regenerate-pom=false
set PROTOCOL_ARGUMENTS=--version=3.1.3 --java --use=./ --output-folder=protocol-tests --sync-methods=all --generate-client-as-impl --add-context-parameter --context-client-method-parameter --generate-sync-async-clients --low-level-client

call autorest %VANILLA_ARGUMENTS% --input-file=https://raw.githubusercontent.com/Azure/autorest.testserver/master/swagger/additionalProperties.json --namespace=fixtures.additionalproperties
call autorest %VANILLA_ARGUMENTS% --input-file=https://raw.githubusercontent.com/Azure/autorest.testserver/master/swagger/body-array.json --namespace=fixtures.bodyarray
Expand Down Expand Up @@ -51,6 +52,8 @@ rem call autorest %ARM_ARGUMENTS% --input-file=https://raw.githubusercontent.com
rem call autorest %ARM_ARGUMENTS% --input-file=https://raw.githubusercontent.com/Azure/autorest.testserver/master/swagger/lro-parameterized-endpoints.json --namespace=fixtures.lroparameterizedendpoints
rem del azure-tests\src\main\java\module-info.java

call autorest $PROTOCOL_ARGUMENTS --input-file=https://raw.githubusercontent.com/Azure/autorest.testserver/master/swagger/body-string.json --namespace=fixtures.bodystring

call autorest --use:. customization-tests/swagger

call autorest --use:. docs/samples/specification/azure_key_credential/readme.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,6 @@ private static ModuleInfo moduleInfo() {

List<ModuleInfo.RequireModule> requireModules = moduleInfo.getRequireModules();
requireModules.add(new ModuleInfo.RequireModule("com.azure.core", true));
requireModules.add(new ModuleInfo.RequireModule("com.azure.core.experimental", true));

List<ModuleInfo.ExportModule> exportModules = moduleInfo.getExportModules();
exportModules.add(new ModuleInfo.ExportModule(settings.getPackage()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ public List<ClientMethod> map(Operation operation) {

List<Parameter> codeModelParameters;
if (settings.isLowLevelClient()) {
codeModelParameters = request.getParameters().stream().filter(Value::isRequired).collect(Collectors.toList());
codeModelParameters = request.getParameters().stream().filter(p ->
p.isRequired() && !(p.getSchema().getType() == Schema.AllSchemaTypes.GROUP)).collect(Collectors.toList());
} else {
codeModelParameters = request.getParameters().stream().filter(p -> !p.isFlattened()).collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,23 @@ public Map<Request, ProxyMethod> map(Operation operation) {
}
}
// RequestOptions
ProxyMethodParameter requestOptions = new ProxyMethodParameter.Builder()
.description("The options to configure the HTTP request before HTTP client sends it")
.wireType(ClassType.RequestOptions)
.clientType(ClassType.RequestOptions)
.name("requestOptions")
.requestParameterLocation(RequestParameterLocation.None)
.requestParameterName("requestOptions")
.alreadyEncoded(true)
.isConstant(false)
.isRequired(false)
.isNullable(false)
.fromClient(false)
.parameterReference("requestOptions")
.build();
parameters.add(requestOptions);
if (settings.isLowLevelClient()) {
ProxyMethodParameter requestOptions = new ProxyMethodParameter.Builder()
.description("The options to configure the HTTP request before HTTP client sends it")
.wireType(ClassType.RequestOptions)
.clientType(ClassType.RequestOptions)
.name("requestOptions")
.requestParameterLocation(RequestParameterLocation.None)
.requestParameterName("requestOptions")
.alreadyEncoded(true)
.isConstant(false)
.isRequired(false)
.isNullable(false)
.fromClient(false)
.parameterReference("requestOptions")
.build();
parameters.add(requestOptions);
}

if (settings.getAddContextParameter()) {
ClassType contextClassType = getContextClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,27 @@ public ProxyMethodParameter map(Parameter parameter) {
}
builder.headerCollectionPrefix(headerCollectionPrefix);

RequestParameterLocation parameterRequestLocation = parameter.getProtocol().getHttp().getIn();
builder.requestParameterLocation(parameterRequestLocation);

boolean parameterIsServiceClientProperty = parameter.getImplementation() == Parameter.ImplementationLocation.CLIENT;
builder.fromClient(parameterIsServiceClientProperty);

Schema ParameterJvWireType = parameter.getSchema();
IType wireType = Mappers.getSchemaMapper().map(ParameterJvWireType);
if (parameter.isNullable() || !parameter.isRequired()) {
wireType = wireType.asNullable();
}
IType clientType = wireType.getClientType();
if (settings.isLowLevelClient() && !(clientType instanceof PrimitiveType)) {
if (parameterRequestLocation == RequestParameterLocation.Body /*&& parameterRequestLocation != RequestParameterLocation.FormData*/) {
clientType = ClassType.BinaryData;
} else {
clientType = ClassType.String;
}
}
builder.clientType(clientType);

RequestParameterLocation parameterRequestLocation = parameter.getProtocol().getHttp().getIn();
builder.requestParameterLocation(parameterRequestLocation);

boolean parameterIsServiceClientProperty = parameter.getImplementation() == Parameter.ImplementationLocation.CLIENT;
builder.fromClient(parameterIsServiceClientProperty);

if (wireType instanceof ListType && settings.shouldGenerateXmlSerialization() && parameterRequestLocation == RequestParameterLocation.Body){
String parameterTypePackage = settings.getPackage(settings.getImplementationSubpackage());
String parameterTypeName = CodeNamer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public class ClassType implements IType {
public static final ClassType AndroidRetryPolicy = new ClassType.Builder().packageName("com.azure.android.core.http.policy").name("RetryPolicy").build();
public static final ClassType JsonPatchDocument =
new ClassType.Builder().knownClass(com.azure.core.models.JsonPatchDocument.class).build();
public static final ClassType BinaryData = new ClassType.Builder().knownClass(com.azure.core.util.BinaryData.class).build();
public static final ClassType BinaryData = new ClassType.Builder().knownClass(com.azure.core.util.BinaryData.class).defaultValueExpressionConverter((String defaultValueExpression) -> java.lang.String.format("BinaryData.fromObject(\"%s\")", defaultValueExpression)).build();
public static final ClassType RequestOptions = new Builder().packageName("com.azure.core.http").name("RequestOptions").build();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ public final String convertFromClientType(String source, String target) {
//C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are created above:
//ORIGINAL LINE: public string ConvertFromClientType(string source, string target, bool alwaysNull = false, bool alwaysNonNull = false)
public final String convertFromClientType(String source, String target, boolean alwaysNull, boolean alwaysNonNull) {
IType clientType = getWireType().getClientType();
if (clientType == getWireType()) {
if (getClientType() == getWireType()) {
return String.format("%1$s %2$s = %3$s;", getWireType(), target, source);
}
if (alwaysNull) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
import com.azure.autorest.extension.base.plugin.JavaSettings;
import com.azure.autorest.model.clientmodel.ArrayType;
import com.azure.autorest.model.clientmodel.ClassType;
import com.azure.autorest.model.clientmodel.ClientEnumValue;
import com.azure.autorest.model.clientmodel.ClientMethod;
import com.azure.autorest.model.clientmodel.ClientMethodParameter;
import com.azure.autorest.model.clientmodel.ClientMethodType;
import com.azure.autorest.model.clientmodel.ClientModel;
import com.azure.autorest.model.clientmodel.ClientModelProperty;
import com.azure.autorest.model.clientmodel.ClientModels;
import com.azure.autorest.model.clientmodel.EnumType;
import com.azure.autorest.model.clientmodel.GenericType;
import com.azure.autorest.model.clientmodel.IType;
import com.azure.autorest.model.clientmodel.ListType;
import com.azure.autorest.model.clientmodel.MapType;
import com.azure.autorest.model.clientmodel.MethodTransformationDetail;
import com.azure.autorest.model.clientmodel.ParameterMapping;
import com.azure.autorest.model.clientmodel.PrimitiveType;
Expand All @@ -30,10 +36,13 @@
import com.azure.core.util.CoreUtils;
import io.netty.handler.codec.http.HttpResponseStatus;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -556,7 +565,13 @@ protected void generateSyncMethod(ClientMethod clientMethod, JavaType typeBlock,
*/
public static void generateJavadoc(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, boolean useFullClassName) {
// interface need a fully-qualified exception class name, since exception is usually only included in ProxyMethod
typeBlock.javadocComment(comment -> generateJavadoc(clientMethod, comment, restAPIMethod, useFullClassName));
typeBlock.javadocComment(comment -> {
if (JavaSettings.getInstance().isLowLevelClient()) {
generateProtocolMethodJavadoc(clientMethod, comment);
} else {
generateJavadoc(clientMethod, comment, restAPIMethod, useFullClassName);
}
});
}

/**
Expand Down Expand Up @@ -601,6 +616,149 @@ protected static String parameterDescriptionOrDefault(ClientMethodParameter para
return paramJavadoc;
}

private static void generateProtocolMethodJavadoc(ClientMethod clientMethod, JavaJavadocComment commentBlock) {
commentBlock.description(clientMethod.getDescription());

List<ProxyMethodParameter> optionalQueryParameters = clientMethod.getProxyMethod().getParameters()
.stream().filter(p -> RequestParameterLocation.Query.equals(p.getRequestParameterLocation()) && !p.getIsRequired())
.collect(Collectors.toList());
if (!optionalQueryParameters.isEmpty()) {
optionalParametersJavadoc("Optional Query Parameters", optionalQueryParameters, commentBlock);
}

List<ProxyMethodParameter> optionalHeaderParameters = clientMethod.getProxyMethod().getParameters()
.stream().filter(p -> RequestParameterLocation.Header.equals(p.getRequestParameterLocation()) && !p.getIsRequired())
.collect(Collectors.toList());
if (!optionalHeaderParameters.isEmpty()) {
optionalParametersJavadoc("Optional Header Parameters", optionalHeaderParameters, commentBlock);
}

Set<IType> typesInJavadoc = new HashSet<>();
clientMethod.getMethodInputParameters()
.stream().filter(p -> RequestParameterLocation.Body.equals(p.getLocation()))
.map(ClientMethodParameter::getClientType)
.findFirst()
.ifPresent(iType -> requestBodySchemaJavadoc(iType, commentBlock, typesInJavadoc));

IType responseBodyType = clientMethod.getProxyMethod().getResponseBodyType();
if (responseBodyType != null && !responseBodyType.equals(PrimitiveType.Void)) {
responseBodySchemaJavadoc(responseBodyType, commentBlock, typesInJavadoc);
}

clientMethod.getProxyMethod().getParameters()
.stream().filter(p -> p.getIsRequired() && !p.getFromClient() && !p.getIsConstant()
&& p.getRequestParameterLocation() != RequestParameterLocation.Body)
.forEach(parameter ->
commentBlock.param(parameter.getName(), parameterDescriptionOrDefault(parameter)));

commentBlock.methodReturns("a DynamicRequest where customizations can be made before sent to the service");
}

private static void optionalParametersJavadoc(String title, List<ProxyMethodParameter> parameters, JavaJavadocComment commentBlock) {
commentBlock.line(String.format("<p><strong>%s</strong></p>", title));
commentBlock.line("<table border=\"1\">");
commentBlock.line(String.format(" <caption>%s</caption>", title));
commentBlock.line(" <tr><th>Name</th><th>Type</th><th>Description</th></tr>");
for (ProxyMethodParameter parameter : parameters) {
commentBlock.line(String.format(" <tr><td>%s</td><td>%s</td><td>%s</td></tr>",
parameter.getName(), CodeNamer.escapeXmlComment(parameter.getClientType().toString()), parameterDescriptionOrDefault(parameter)));
}
commentBlock.line("</table>");
}

private static void requestBodySchemaJavadoc(IType requestBodyType, JavaJavadocComment commentBlock, Set<IType> typesInJavadoc) {
if (requestBodyType == null) {
return;
}
commentBlock.line("<p><strong>Request Body Schema</strong></p>");
commentBlock.line("<pre>{@code");
bodySchemaJavadoc(requestBodyType, commentBlock, "", null, typesInJavadoc);
commentBlock.line("}</pre>");
}

private static void responseBodySchemaJavadoc(IType responseBodyType, JavaJavadocComment commentBlock, Set<IType> typesInJavadoc) {
if (responseBodyType == null) {
return;
}
commentBlock.line("<p><strong>Response Body Schema</strong></p>");
commentBlock.line("<pre>{@code");
bodySchemaJavadoc(responseBodyType, commentBlock, "", null, typesInJavadoc);
commentBlock.line("}</pre>");
}

private static void bodySchemaJavadoc(IType type, JavaJavadocComment commentBlock, String indent, String name, Set<IType> typesInJavadoc) {
String nextIndent = indent + " ";
if (type instanceof ClassType
&& ((ClassType) type).getPackage().startsWith(JavaSettings.getInstance().getPackage())
&& !typesInJavadoc.contains(type)) {
typesInJavadoc.add(type);
ClientModel model = ClientModels.Instance.getModel(((ClassType) type).getName());
if (name != null) {
commentBlock.line(indent + name + ": {");
} else {
commentBlock.line(indent + "{");
}
List<ClientModelProperty> properties = new ArrayList<>();
traverseProperties(model, properties);
for (ClientModelProperty property : properties) {
bodySchemaJavadoc(property.getClientType(), commentBlock, nextIndent, property.getName(), typesInJavadoc);
}
commentBlock.line(indent + "}");
} else if (typesInJavadoc.contains(type)) {
if (name != null) {
commentBlock.line(indent + name + ": (recursive schema, see " + name + " above)");
} else {
commentBlock.line(indent + "(recursive schema, see above)");
}
} else if (type instanceof ListType) {
if (name != null) {
commentBlock.line(indent + name + ": [");
} else {
commentBlock.line(indent + "[");
}
bodySchemaJavadoc(((ListType) type).getElementType(), commentBlock, nextIndent, null, typesInJavadoc);
commentBlock.line(indent + "]");
} else if (type instanceof EnumType) {
String values = ((EnumType) type).getValues().stream()
.map(ClientEnumValue::getValue)
.collect(Collectors.joining("/"));
if (name != null) {
commentBlock.line(indent + name + ": String(" + values + ")");
} else {
commentBlock.line(indent + "String(" + values + ")");
}
} else if (type instanceof MapType) {
if (name != null) {
commentBlock.line(indent + name + ": {");
} else {
commentBlock.line(indent + "{");
}
bodySchemaJavadoc(((MapType) type).getValueType(), commentBlock, nextIndent, "String", typesInJavadoc);
commentBlock.line(indent + "}");
} else {
if (name != null) {
commentBlock.line(indent + name + ": " + type.toString());
} else {
commentBlock.line(indent + type.toString());
}
}
}

private static void traverseProperties(ClientModel model, List<ClientModelProperty> properties) {
if (model.getParentModelName() != null) {
traverseProperties(ClientModels.Instance.getModel(model.getParentModelName()), properties);
}
properties.addAll(model.getProperties());
}

private static String parameterDescriptionOrDefault(ProxyMethodParameter parameter) {
String paramJavadoc = parameter.getDescription();
if (CoreUtils.isNullOrEmpty(paramJavadoc)) {
paramJavadoc = String.format("The %1$s parameter", parameter.getName());
}
return CodeNamer.escapeXmlComment(paramJavadoc);
}

protected void generatePagedAsyncSinglePage(ClientMethod clientMethod, JavaType typeBlock, ProxyMethod restAPIMethod, JavaSettings settings) {
typeBlock.annotation("ServiceMethod(returns = ReturnType.SINGLE)");

Expand Down
Loading