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

http-client-java, support (non-nested) continuationToken for unbranded #6143

Merged
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8f04696
fix interface
weidongxu-microsoft Feb 14, 2025
25e05d0
test code and initial interface of PageableContinuationToken
weidongxu-microsoft Feb 14, 2025
f7a0c45
handle PagedResponse
weidongxu-microsoft Feb 18, 2025
224703c
handle query/header
weidongxu-microsoft Feb 18, 2025
03d005c
hack for test
weidongxu-microsoft Feb 19, 2025
928ab36
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Feb 20, 2025
32137b1
refactor to use ModelPropertySegment, instead of itemName/itemSeriali…
weidongxu-microsoft Feb 20, 2025
3785ba3
hide continuationToken parameter from API
weidongxu-microsoft Feb 20, 2025
d2c8831
add throw calls on not support items in PagingOptions
weidongxu-microsoft Feb 20, 2025
18f8ae0
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Feb 25, 2025
feb1c68
use tcgc for continuationToken
weidongxu-microsoft Feb 25, 2025
a6cb649
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Feb 25, 2025
c168edd
enable for unbranded
weidongxu-microsoft Feb 25, 2025
0cd5416
fix
weidongxu-microsoft Feb 25, 2025
9ce5472
bump http-specs and regen
weidongxu-microsoft Feb 25, 2025
905abfe
update test
weidongxu-microsoft Feb 25, 2025
d9924ae
test
weidongxu-microsoft Feb 25, 2025
c85e84d
fix
weidongxu-microsoft Feb 25, 2025
c6b7a2c
comment
weidongxu-microsoft Feb 25, 2025
59e1d38
add test for page2
weidongxu-microsoft Feb 25, 2025
c6d3eb3
revert Main
weidongxu-microsoft Feb 25, 2025
4aa4b0f
bump jar
weidongxu-microsoft Feb 25, 2025
6af2a02
bump version
weidongxu-microsoft Feb 25, 2025
9d81f37
test for throws if non-supported param is set
weidongxu-microsoft Feb 25, 2025
e15b44e
fix test package ref
weidongxu-microsoft Feb 25, 2025
df60efe
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Feb 27, 2025
77b4d3c
0.1.12
weidongxu-microsoft Feb 27, 2025
cbf31a6
review feedback
weidongxu-microsoft Feb 28, 2025
87491a1
simplify paged op logic, do early return
weidongxu-microsoft Feb 28, 2025
77234d1
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Feb 28, 2025
34f4cb6
Fix typo in comment: "mutally" to "mutually"
weidongxu-microsoft Mar 4, 2025
89b3638
Merge branch 'main' into http-client-java_continuation-token
weidongxu-microsoft Mar 4, 2025
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
Prev Previous commit
Next Next commit
refactor to use ModelPropertySegment, instead of itemName/itemSeriali…
…zedNmae/itemType
  • Loading branch information
weidongxu-microsoft committed Feb 20, 2025
commit 32137b1e67cb79e5ea265ebe833fc66d2c063298
Original file line number Diff line number Diff line change
@@ -35,14 +35,14 @@
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.MethodParameter;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.MethodPollingDetails;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.MethodTransformationDetail;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ModelPropertySegment;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ParameterMapping;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.PrimitiveType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethod;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethodParameter;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ReturnValue;
import com.microsoft.typespec.http.client.generator.core.model.javamodel.JavaVisibility;
import com.microsoft.typespec.http.client.generator.core.util.ClientModelUtil;
import com.microsoft.typespec.http.client.generator.core.util.CodeNamer;
import com.microsoft.typespec.http.client.generator.core.util.MethodNamer;
import com.microsoft.typespec.http.client.generator.core.util.MethodUtil;
import com.microsoft.typespec.http.client.generator.core.util.ReturnTypeDescriptionAssembler;
@@ -327,31 +327,32 @@ private List<ClientMethod> createClientMethods(Operation operation, boolean isPr
.methodPageDetails(null);

if (isPageable) {
String pageableItemName = getPageableItemName(operation.getExtensions().getXmsPageable(),
proxyMethod.getRawResponseBodyType() != null
? proxyMethod.getRawResponseBodyType()
: proxyMethod.getResponseBodyType());
if (pageableItemName == null) {
IType responseType = proxyMethod.getRawResponseBodyType() != null
? proxyMethod.getRawResponseBodyType()
: proxyMethod.getResponseBodyType();
ModelPropertySegment itemPropertyReference
= getPageableItem(operation.getExtensions().getXmsPageable(), responseType);
if (itemPropertyReference == null) {
// There is no pageable item name for this operation, skip it.
continue;
}

// If the ProxyMethod is synchronous perform a complete generation of synchronous pageable APIs.
if (proxyMethod.isSync()) {
createSyncPageableClientMethods(operation, isProtocolMethod, settings, methods, builder,
returnTypeHolder, proxyMethod, parameters, pageableItemName, generateOnlyRequiredParameters,
defaultOverloadType);
returnTypeHolder, proxyMethod, parameters, itemPropertyReference,
generateOnlyRequiredParameters, defaultOverloadType);
} else {
// Otherwise, perform a complete generation of asynchronous pageable APIs.
// Then if SyncMethodsGeneration is enabled and Sync Stack is not perform synchronous pageable
// API generation based on SyncMethodsGeneration configuration.
createAsyncPageableClientMethods(operation, isProtocolMethod, settings, methods, builder,
returnTypeHolder, proxyMethod, parameters, pageableItemName, generateOnlyRequiredParameters,
defaultOverloadType);
returnTypeHolder, proxyMethod, parameters, itemPropertyReference,
generateOnlyRequiredParameters, defaultOverloadType);

if (settings.isGenerateSyncMethods() && !settings.isSyncStackEnabled()) {
createSyncPageableClientMethods(operation, isProtocolMethod, settings, methods, builder,
returnTypeHolder, proxyMethod, parameters, pageableItemName,
returnTypeHolder, proxyMethod, parameters, itemPropertyReference,
generateOnlyRequiredParameters, defaultOverloadType);
}
}
@@ -698,7 +699,7 @@ private static List<Parameter> getCodeModelParameters(Request request, boolean i

private void createAsyncPageableClientMethods(Operation operation, boolean isProtocolMethod, JavaSettings settings,
List<ClientMethod> methods, ClientMethod.Builder builder, ReturnTypeHolder returnTypeHolder,
ProxyMethod proxyMethod, List<ClientMethodParameter> parameters, String pageableItemName,
ProxyMethod proxyMethod, List<ClientMethodParameter> parameters, ModelPropertySegment itemPropertyReference,
boolean generateClientMethodWithOnlyRequiredParameters, MethodOverloadType defaultOverloadType) {

ReturnValue singlePageReturnValue = createPagingAsyncSinglePageReturnValue(operation,
@@ -710,13 +711,13 @@ private void createAsyncPageableClientMethods(Operation operation, boolean isPro
includesContext, isProtocolMethod);

createPageableClientMethods(operation, isProtocolMethod, settings, methods, builder, proxyMethod, parameters,
pageableItemName, false, singlePageReturnValue, nextPageReturnValue, visibilityFunction,
itemPropertyReference, false, singlePageReturnValue, nextPageReturnValue, visibilityFunction,
getContextParameter(isProtocolMethod), generateClientMethodWithOnlyRequiredParameters, defaultOverloadType);
}

private void createSyncPageableClientMethods(Operation operation, boolean isProtocolMethod, JavaSettings settings,
List<ClientMethod> methods, Builder builder, ReturnTypeHolder returnTypeHolder, ProxyMethod proxyMethod,
List<ClientMethodParameter> parameters, String pageableItemName,
List<ClientMethodParameter> parameters, ModelPropertySegment itemPropertyReference,
boolean generateClientMethodWithOnlyRequiredParameters, MethodOverloadType defaultOverloadType) {

ReturnValue singlePageReturnValue = createPagingSyncSinglePageReturnValue(operation,
@@ -727,22 +728,20 @@ private void createSyncPageableClientMethods(Operation operation, boolean isProt
includesContext, isProtocolMethod);

createPageableClientMethods(operation, isProtocolMethod, settings, methods, builder, proxyMethod, parameters,
pageableItemName, true, singlePageReturnValue, nextPageReturnValue, visibilityFunction,
itemPropertyReference, true, singlePageReturnValue, nextPageReturnValue, visibilityFunction,
getContextParameter(isProtocolMethod), generateClientMethodWithOnlyRequiredParameters, defaultOverloadType);
}

private static void createPageableClientMethods(Operation operation, boolean isProtocolMethod,
JavaSettings settings, List<ClientMethod> methods, Builder builder, ProxyMethod proxyMethod,
List<ClientMethodParameter> parameters, String pageableItemName, boolean isSync,
List<ClientMethodParameter> parameters, ModelPropertySegment itemPropertyReference, boolean isSync,
ReturnValue singlePageReturnValue, ReturnValue nextPageReturnValue, MethodVisibilityFunction visibilityFunction,
ClientMethodParameter contextParameter, boolean generateClientMethodWithOnlyRequiredParameters,
MethodOverloadType defaultOverloadType) {

MethodNamer methodNamer = resolveMethodNamer(proxyMethod, operation.getConvenienceApi(), isProtocolMethod);

Operation nextOperation = operation.getExtensions().getXmsPageable().getNextOperation();
String nextLinkName = operation.getExtensions().getXmsPageable().getNextLinkName();
String itemName = operation.getExtensions().getXmsPageable().getItemName();
ClientMethodType nextMethodType
= isSync ? ClientMethodType.PagingSyncSinglePage : ClientMethodType.PagingAsyncSinglePage;

@@ -760,14 +759,15 @@ private static void createPageableClientMethods(Operation operation, boolean isP
? null
: nextMethods.stream().filter(m -> m.getType() == nextMethodType).findFirst().orElse(null);

IType nextLinkType = getPageableNextLinkType(operation.getExtensions().getXmsPageable(),
(proxyMethod.getRawResponseBodyType() != null
? proxyMethod.getRawResponseBodyType()
: proxyMethod.getResponseBodyType()).toString());
IType responseType = proxyMethod.getRawResponseBodyType() != null
? proxyMethod.getRawResponseBodyType()
: proxyMethod.getResponseBodyType();
ModelPropertySegment nextLinkPropertyReference
= getPageableNextLink(operation.getExtensions().getXmsPageable(), responseType);

MethodPageDetails details = new MethodPageDetails(CodeNamer.getPropertyName(nextLinkName), nextLinkType,
pageableItemName, nextMethod, lroIntermediateType, nextLinkName, itemName,
operation.getExtensions().getXmsPageable().getContinuationToken());
MethodPageDetails details = new MethodPageDetails(itemPropertyReference, nextLinkPropertyReference, nextMethod,
lroIntermediateType, MethodPageDetails.ContinuationToken.fromContinuationToken(
operation.getExtensions().getXmsPageable().getContinuationToken(), responseType));
builder.methodPageDetails(details);

String pageMethodName
@@ -836,9 +836,9 @@ private static void createPageableClientMethods(Operation operation, boolean isP
.orElse(null);

if (nextMethod != null) {
detailsWithContext = new MethodPageDetails(CodeNamer.getPropertyName(nextLinkName), nextLinkType,
pageableItemName, nextMethod, lroIntermediateType, nextLinkName, itemName,
operation.getExtensions().getXmsPageable().getContinuationToken());
detailsWithContext = new MethodPageDetails(itemPropertyReference, nextLinkPropertyReference, nextMethod,
lroIntermediateType, MethodPageDetails.ContinuationToken.fromContinuationToken(
operation.getExtensions().getXmsPageable().getContinuationToken(), responseType));
}
}

@@ -1589,30 +1589,12 @@ protected static void addClientMethodWithContext(List<ClientMethod> methods, Bui
builder.parameters(parameters);
}

private static String getPageableItemName(XmsPageable xmsPageable, IType responseBodyType) {
ClientModel responseBodyModel = ClientModelUtil.getClientModel(responseBodyType.toString());
return Stream
.concat(responseBodyModel.getProperties().stream(),
ClientModelUtil.getParentProperties(responseBodyModel).stream())
.filter(p -> p.getSerializedName().equals(xmsPageable.getItemName()))
.map(ClientModelProperty::getName)
.findAny()
.orElse(null);
private static ModelPropertySegment getPageableItem(XmsPageable xmsPageable, IType responseBodyType) {
return ClientModelUtil.getModelPropertySegment(responseBodyType, xmsPageable.getItemName());
}

private static IType getPageableNextLinkType(XmsPageable xmsPageable, String clientModelName) {
ClientModel responseBodyModel = ClientModelUtil.getClientModel(clientModelName);
IType nextLinkType = responseBodyModel.getProperties()
.stream()
.filter(p -> p.getSerializedName().equals(xmsPageable.getNextLinkName()))
.map(ClientModelProperty::getClientType)
.findAny()
.orElse(null);
if (nextLinkType == null && !CoreUtils.isNullOrEmpty(responseBodyModel.getParentModelName())) {
// try find nextLink property in parent model
nextLinkType = getPageableNextLinkType(xmsPageable, responseBodyModel.getParentModelName());
}
return nextLinkType;
private static ModelPropertySegment getPageableNextLink(XmsPageable xmsPageable, IType responseBodyType) {
return ClientModelUtil.getModelPropertySegment(responseBodyType, xmsPageable.getNextLinkName());
}

private IType getPollingIntermediateType(JavaSettings.PollingDetails details, IType syncReturnType) {
Original file line number Diff line number Diff line change
@@ -3,68 +3,108 @@

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

import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Property;
import com.microsoft.typespec.http.client.generator.core.extension.model.extensionmodel.PageableContinuationToken;
import com.microsoft.typespec.http.client.generator.core.mapper.ProxyParameterMapper;
import com.microsoft.typespec.http.client.generator.core.util.ClientModelUtil;
import java.util.ArrayList;
import java.util.List;

/**
* A page class that contains results that are received from a service request.
*/
public class MethodPageDetails {
public final class MethodPageDetails {
/**
* Get whether or not this method is a request to get the next page of a sequence of pages.
*/
private final String nextLinkName;
private final IType nextLinkType;

private final String itemName;

/**
* Serialized nextLink name. It is the name in swagger and in response.
*/
private final String serializedNextLinkName;
/**
* Serialized item name. It is the name in swagger and in response.
*/
private final String serializedItemName;
private final ModelPropertySegment nextLinkPropertyReference;
private final ModelPropertySegment itemPropertyReference;

private final 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 final IType lroIntermediateType;

private PageableContinuationToken continuationToken;
public static final class ContinuationToken {
private final ProxyMethodParameter requestParameter;
private final List<ModelPropertySegment> responsePropertyReference;
private final String responseHeaderSerializedName;

private ContinuationToken(PageableContinuationToken continuationToken, IType responseBodyType) {
this.requestParameter = ProxyParameterMapper.getInstance().map(continuationToken.getParameter());
this.responseHeaderSerializedName = continuationToken.getResponseHeader() == null
? null
: continuationToken.getResponseHeader().getHeader();

List<ModelPropertySegment> responsePropertyReference = null;
if (continuationToken.getResponseProperty() != null) {
responsePropertyReference = new ArrayList<>();
IType modelType = responseBodyType;
for (Property p : continuationToken.getResponseProperty()) {
ModelPropertySegment segment
= ClientModelUtil.getModelPropertySegment(modelType, p.getSerializedName());
if (segment != null) {
responsePropertyReference.add(segment);
} else {
throw new RuntimeException(
String.format("Property of serialized name '%s' is not found in model '%s'.",
p.getSerializedName(), modelType.toString()));
}

modelType = segment.getProperty().getClientType();
}
}
this.responsePropertyReference = responsePropertyReference;
}

public static ContinuationToken fromContinuationToken(PageableContinuationToken continuationToken,
IType responseBodyType) {
return continuationToken == null ? null : new ContinuationToken(continuationToken, responseBodyType);
}

public ProxyMethodParameter getRequestParameter() {
return requestParameter;
}

public List<ModelPropertySegment> getResponsePropertyReference() {
return responsePropertyReference;
}

public String getResponseHeaderSerializedName() {
return responseHeaderSerializedName;
}
}

private ContinuationToken continuationToken;

public MethodPageDetails(String nextLinkName, IType nextLinkType, String itemName, ClientMethod nextMethod,
IType lroIntermediateType, String serializedNextLinkName, String serializedItemName,
PageableContinuationToken continuationToken) {
this.nextLinkName = nextLinkName;
this.nextLinkType = nextLinkType;
this.itemName = itemName;
public MethodPageDetails(ModelPropertySegment itemPropertyReference, ModelPropertySegment nextLinkPropertyReference,
ClientMethod nextMethod, IType lroIntermediateType, ContinuationToken continuationToken) {
this.itemPropertyReference = itemPropertyReference;
this.nextLinkPropertyReference = nextLinkPropertyReference;
this.nextMethod = nextMethod;
this.lroIntermediateType = lroIntermediateType;
this.serializedNextLinkName = serializedNextLinkName;
this.serializedItemName = serializedItemName;
this.continuationToken = continuationToken;
}

public String getNextLinkName() {
return nextLinkName;
return nextLinkPropertyReference == null ? null : nextLinkPropertyReference.getProperty().getName();
}

public IType getNextLinkType() {
return nextLinkType;
return nextLinkPropertyReference == null ? null : nextLinkPropertyReference.getProperty().getClientType();
}

public String getSerializedNextLinkName() {
return serializedNextLinkName;
return nextLinkPropertyReference.getProperty().getSerializedName();
}

public String getItemName() {
return itemName;
return itemPropertyReference == null ? null : itemPropertyReference.getProperty().getName();
}

public String getSerializedItemName() {
return serializedItemName;
return itemPropertyReference == null ? null : itemPropertyReference.getProperty().getSerializedName();
}

public ClientMethod getNextMethod() {
@@ -76,10 +116,10 @@ public IType getLroIntermediateType() {
}

public boolean nonNullNextLink() {
return nextLinkName != null && !nextLinkName.isEmpty();
return getNextLinkName() != null && !getNextLinkName().isEmpty();
}

public PageableContinuationToken getContinuationToken() {
public ContinuationToken getContinuationToken() {
return continuationToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

/**
* A segment in property reference. It can be chained as a List, if the reference contains multiple model/property.
* <p>
* E.g. a reference to "links", "nextLink" in response body would be a List of 2 ModelPropertySegment.
*/
public class ModelPropertySegment {

private final ClientModel model;
private final ClientModelProperty property;

public ModelPropertySegment(ClientModel model, ClientModelProperty property) {
this.model = model;
this.property = property;
}

/**
* Gets the model of the property. It is possible that the property is actually specified in its parent model.
*
* @return the model of the property
*/
public ClientModel getModel() {
return model;
}

/**
* Gets the property.
*
* @return the property
*/
public ClientModelProperty getProperty() {
return property;
}
}
Loading
Oops, something went wrong.