Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/http-client-java"
---

Document response headers in protocol API Javadocs.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public ClientMethodParameter map(Parameter parameter, boolean isProtocolMethod)
builder.rawType(wireType);

if (isProtocolMethod) {
wireType = SchemaUtil.removeModelFromParameter(parameter.getProtocol().getHttp().getIn(), wireType);
wireType = SchemaUtil.removeModelFromClientType(parameter.getProtocol().getHttp().getIn(), wireType);
}

builder.wireType(wireType).annotations(new ArrayList<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public ClientMethodParameter map(Parameter parameter, boolean isProtocolMethod)
wireType = Mappers.getSchemaMapper().map(parameter.getSchema());
}
if (isProtocolMethod) {
wireType = SchemaUtil.removeModelFromParameter(parameter.getProtocol().getHttp().getIn(), wireType);
wireType = SchemaUtil.removeModelFromClientType(parameter.getProtocol().getHttp().getIn(), wireType);
}
if (parameter.isNullable() || !parameter.isRequired()) {
builder.wireType(wireType.asNullable());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
package com.microsoft.typespec.http.client.generator.core.mapper;

import com.microsoft.typespec.http.client.generator.core.Javagen;
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Header;
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Operation;
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Request;
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.RequestParameterLocation;
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Response;
import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings;
import com.microsoft.typespec.http.client.generator.core.extension.plugin.PluginLogger;
Expand All @@ -16,6 +18,7 @@
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethod;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethodExample;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethodParameter;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethodResponseHeader;
import com.microsoft.typespec.http.client.generator.core.util.MethodUtil;
import com.microsoft.typespec.http.client.generator.core.util.SchemaUtil;
import com.microsoft.typespec.http.client.generator.core.util.XmsExampleWrapper;
Expand All @@ -25,6 +28,7 @@
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
Expand Down Expand Up @@ -68,6 +72,7 @@ public Map<Request, List<ProxyMethod>> map(Operation operation) {

final List<Integer> expectedStatusCodes = getExpectedResponseStatusCodes(operation);
builder.responseExpectedStatusCodes(expectedStatusCodes);
builder.responseHeaders(getResponseHeaders(operation, settings));
Comment thread
weidongxu-microsoft marked this conversation as resolved.
buildExpectedResponseFields(operation, settings, builder);
buildUnexpectedResponseExceptionFields(builder, operation, expectedStatusCodes, settings);
builder.responseContentTypes(getResponseContentTypes(operation));
Expand Down Expand Up @@ -130,6 +135,45 @@ private static List<Integer> getExpectedResponseStatusCodes(Operation operation)
.collect(Collectors.toList());
}

protected static List<ProxyMethodResponseHeader> getResponseHeaders(Operation operation, JavaSettings settings) {
if (!SchemaUtil.responseContainsHeaderSchemas(operation, settings)) {
return List.of();
}

Map<String, ProxyMethodResponseHeader> responseHeaders = new LinkedHashMap<>();
operation.getResponses()
.stream()
.filter(response -> response.getProtocol() != null
&& response.getProtocol().getHttp() != null
&& response.getProtocol().getHttp().getHeaders() != null)
.flatMap(response -> response.getProtocol().getHttp().getHeaders().stream())
.filter(header -> header.getSchema() != null)
.forEach(header -> responseHeaders.putIfAbsent(header.getHeader().toLowerCase(Locale.ROOT),
mapResponseHeader(header)));
return new ArrayList<>(responseHeaders.values());
}

private static ProxyMethodResponseHeader mapResponseHeader(Header header) {
IType clientType = Mappers.getSchemaMapper().map(header.getSchema()).getClientType();
Comment thread
weidongxu-microsoft marked this conversation as resolved.
if (Mappers.getProxyParameterMapper().isRemoveModelFromClientType(clientType)) {
clientType = SchemaUtil.removeModelFromClientType(RequestParameterLocation.HEADER, clientType);
}
String description = null;
if (header.getLanguage() != null && header.getLanguage().getDefault() != null) {
description = header.getLanguage().getDefault().getDescription();
}
if (CoreUtils.isNullOrEmpty(description)) {
description = header.getSchema().getDescription();
}
if (CoreUtils.isNullOrEmpty(description)) {
description = String.format("The %s response header.", header.getHeader());
}
return new ProxyMethodResponseHeader.Builder().serializedName(header.getHeader())
.clientType(clientType)
.description(description)
.build();
}

/**
* Resolve and update the builder with the expected response (successful response) body type, wire type and the
* return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public ProxyMethodParameter map(Parameter parameter) {

IType clientType = wireType.getClientType();

if (isRemoveModelFromParameter(parameter, clientType)) {
clientType = SchemaUtil.removeModelFromParameter(parameterRequestLocation, clientType);
if (isRemoveModelFromClientType(parameter, clientType)) {
clientType = SchemaUtil.removeModelFromClientType(parameterRequestLocation, clientType);
}

builder.clientType(clientType);
Expand All @@ -90,8 +90,8 @@ public ProxyMethodParameter map(Parameter parameter) {
if (parameterRequestLocation
!= RequestParameterLocation.BODY /* && parameterRequestLocation != RequestParameterLocation.FormData */) {
wireType = ClassType.STRING;
} else if (isRemoveModelFromParameter(parameter, wireType)) {
wireType = SchemaUtil.removeModelFromParameter(parameterRequestLocation, wireType);
} else if (isRemoveModelFromClientType(parameter, wireType)) {
wireType = SchemaUtil.removeModelFromClientType(parameterRequestLocation, wireType);
}
} else if (wireType instanceof IterableType
&& parameter.getProtocol().getHttp().getIn()
Expand All @@ -104,8 +104,8 @@ public ProxyMethodParameter map(Parameter parameter) {
} else {
wireType = ClassType.STRING;
}
} else if (isRemoveModelFromParameter(parameter, wireType)) {
wireType = SchemaUtil.removeModelFromParameter(parameterRequestLocation, wireType);
} else if (isRemoveModelFromClientType(parameter, wireType)) {
wireType = SchemaUtil.removeModelFromClientType(parameterRequestLocation, wireType);
}
builder.wireType(wireType);

Expand Down Expand Up @@ -184,7 +184,11 @@ public ProxyMethodParameter map(Parameter parameter) {
return builder.build();
}

protected boolean isRemoveModelFromParameter(Parameter parameter, IType clientType) {
protected boolean isRemoveModelFromClientType(Parameter parameter, IType clientType) {
return isRemoveModelFromClientType(clientType);
}

protected boolean isRemoveModelFromClientType(IType clientType) {
return JavaSettings.getInstance().isDataPlaneClient();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ protected List<ServiceClientProperty> processClientProperties(Client client, Str
String serviceClientPropertyName = CodeNamer.getPropertyName(p.getLanguage().getJava().getName());

IType serviceClientPropertyClientType = Mappers.getSchemaMapper().map(p.getSchema());
if (isRemoveModelFromParameter(p, serviceClientPropertyClientType)) {
if (isRemoveModelFromClientType(p, serviceClientPropertyClientType)) {
// mostly for Enum to String
serviceClientPropertyClientType = SchemaUtil.removeModelFromParameter(RequestParameterLocation.URI,
serviceClientPropertyClientType = SchemaUtil.removeModelFromClientType(RequestParameterLocation.URI,
serviceClientPropertyClientType);
}
if (p.isNullable() && serviceClientPropertyClientType != null) {
Expand Down Expand Up @@ -250,7 +250,7 @@ protected List<ServiceClientProperty> processClientProperties(Client client, Str
return serviceClientProperties;
}

protected boolean isRemoveModelFromParameter(Parameter parameter, IType type) {
protected boolean isRemoveModelFromClientType(Parameter parameter, IType type) {
return JavaSettings.getInstance().isDataPlaneClient();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public Map<Request, List<ProxyMethod>> map(Operation operation) {
.sorted()
.collect(Collectors.toList());
builder.responseExpectedStatusCodes(expectedStatusCodes);
builder.responseHeaders(getResponseHeaders(operation, settings));

IType responseBodyType = MapperUtils.getExpectedResponseBodyType(operation, settings);
builder.responseBodyType(responseBodyType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

package com.microsoft.typespec.http.client.generator.core.mapper.clientcore;

import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Parameter;
import com.microsoft.typespec.http.client.generator.core.mapper.ProxyParameterMapper;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.IType;

Expand All @@ -18,7 +17,7 @@ public static ClientCoreProxyParameterMapper getInstance() {
}

@Override
protected boolean isRemoveModelFromParameter(Parameter parameter, IType clientType) {
protected boolean isRemoveModelFromClientType(IType clientType) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public class ProxyMethod {
*/
private final Set<String> responseContentTypes;

private final List<ProxyMethodResponseHeader> responseHeaders;

private final Map<String, ProxyMethodExample> examples;

private final List<String> specialHeaders;
Expand Down Expand Up @@ -123,6 +125,7 @@ public ProxyMethod.Builder newBuilder() {
.rawResponseBodyType(rawResponseBodyType)
.isResumable(isResumable)
.responseContentTypes(responseContentTypes)
.responseHeaders(responseHeaders)
.examples(examples)
.specialHeaders(specialHeaders)
.operationId(operationId)
Expand Down Expand Up @@ -159,9 +162,9 @@ private ProxyMethod(String requestContentType, IType returnType, HttpMethod http
Map<ClassType, List<Integer>> unexpectedResponseExceptionTypes, String name,
List<ProxyMethodParameter> parameters, List<ProxyMethodParameter> allParameters, String description,
IType returnValueWireType, IType responseBodyType, IType rawResponseBodyType, boolean isResumable,
Set<String> responseContentTypes, String operationId, Map<String, ProxyMethodExample> examples,
List<String> specialHeaders, boolean isSync, String baseName, boolean customHeaderIgnored,
String implementation) {
Set<String> responseContentTypes, List<ProxyMethodResponseHeader> responseHeaders, String operationId,
Map<String, ProxyMethodExample> examples, List<String> specialHeaders, boolean isSync, String baseName,
boolean customHeaderIgnored, String implementation) {
this.requestContentType = requestContentType;
this.returnType = returnType;
this.httpMethod = httpMethod;
Expand All @@ -179,6 +182,7 @@ private ProxyMethod(String requestContentType, IType returnType, HttpMethod http
this.rawResponseBodyType = rawResponseBodyType;
this.isResumable = isResumable;
this.responseContentTypes = responseContentTypes;
this.responseHeaders = responseHeaders;
this.operationId = operationId;
this.examples = examples;
this.specialHeaders = specialHeaders;
Expand Down Expand Up @@ -284,6 +288,10 @@ public final Set<String> getResponseContentTypes() {
return responseContentTypes;
}

public List<ProxyMethodResponseHeader> getResponseHeaders() {
return responseHeaders;
}

public String getOperationId() {
return operationId;
}
Expand Down Expand Up @@ -359,6 +367,7 @@ public ProxyMethod toSync() {
.unexpectedResponseExceptionTypes(this.getUnexpectedResponseExceptionTypes())
.allParameters(allSyncParams)
.responseContentTypes(this.getResponseContentTypes())
.responseHeaders(this.getResponseHeaders())
.responseExpectedStatusCodes(this.getResponseExpectedStatusCodes())
.isSync(true)
.customHeaderIgnored(this.customHeaderIgnored)
Expand Down Expand Up @@ -523,6 +532,7 @@ public static class Builder {
protected IType rawResponseBodyType;
protected boolean isResumable;
protected Set<String> responseContentTypes;
protected List<ProxyMethodResponseHeader> responseHeaders;
protected Map<String, ProxyMethodExample> examples;
protected String operationId;
protected List<String> specialHeaders;
Expand Down Expand Up @@ -733,6 +743,17 @@ public Builder responseContentTypes(Set<String> responseContentTypes) {
return this;
}

/**
* Sets the response headers.
*
* @param responseHeaders the response headers.
* @return the Builder itself
*/
public Builder responseHeaders(List<ProxyMethodResponseHeader> responseHeaders) {
this.responseHeaders = responseHeaders;
return this;
}

/**
* Sets the examples for the method.
*
Expand Down Expand Up @@ -790,8 +811,9 @@ public ProxyMethod build() {
CollectionUtil.toImmutableMapOfList(unexpectedResponseExceptionTypes), name,
CollectionUtil.toImmutableList(parameters), CollectionUtil.toImmutableList(allParameters), description,
returnValueWireType, responseBodyType, rawResponseBodyType, isResumable,
CollectionUtil.toImmutableSet(responseContentTypes), operationId, examples,
CollectionUtil.toImmutableList(specialHeaders), isSync, baseName, customHeaderIgnored, implementation);
CollectionUtil.toImmutableSet(responseContentTypes), CollectionUtil.toImmutableList(responseHeaders),
operationId, examples, CollectionUtil.toImmutableList(specialHeaders), isSync, baseName,
customHeaderIgnored, implementation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.typespec.http.client.generator.core.model.clientmodel;

/**
* A response header returned by a {@link ProxyMethod}.
*/
public final class ProxyMethodResponseHeader {
private final String serializedName;

// Javadoc only needs the public client type. Add raw and wire types if response generation needs them in future.
private final IType clientType;

private final String description;

private ProxyMethodResponseHeader(String serializedName, IType clientType, String description) {
this.serializedName = serializedName;
this.clientType = clientType;
this.description = description;
}

/**
* Gets the serialized header name.
*
* @return the serialized header name.
*/
public String getSerializedName() {
return serializedName;
}

/**
* Gets the client type of the header value.
*
* @return the client type of the header value.
*/
public IType getClientType() {
return clientType;
}

/**
* Gets the header description.
*
* @return the header description.
*/
public String getDescription() {
return description;
}

/**
* Builder for {@link ProxyMethodResponseHeader}.
*/
public static final class Builder {
private String serializedName;
private IType clientType;
private String description;

/**
* Sets the serialized header name.
*
* @param serializedName the serialized header name.
* @return the Builder itself.
*/
public Builder serializedName(String serializedName) {
this.serializedName = serializedName;
return this;
}

/**
* Sets the client type of the header value.
*
* @param clientType the client type of the header value.
* @return the Builder itself.
*/
public Builder clientType(IType clientType) {
this.clientType = clientType;
return this;
}

/**
* Sets the header description.
*
* @param description the header description.
* @return the Builder itself.
*/
public Builder description(String description) {
this.description = description;
return this;
}

/**
* Builds the response header.
*
* @return the response header.
*/
public ProxyMethodResponseHeader build() {
return new ProxyMethodResponseHeader(serializedName, clientType, description);
}
}
}
Loading
Loading