Skip to content

Refactor ClientMethod to Support Immutable Builders and Enhance Polling & Paging Metadata #7015

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

Merged
merged 44 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
17bb495
enable newBuilder for the ClientMethod
anuchandy Apr 16, 2025
7b37e69
Adding JavaDocs, and reuse ProxyMethod::hasParameterOfType in ProxyMe…
anuchandy Apr 19, 2025
4db0b3a
enable immutability in ClientMethodMapper - phase1
anuchandy Apr 21, 2025
8f914d4
enable immutability in ClientMethodMapper - phase2
anuchandy Apr 21, 2025
135796e
include polling-details in the shared lroBaseMethod
anuchandy Apr 21, 2025
fb84d2e
enable immutability in ClientMethodMapper - phase3
anuchandy Apr 21, 2025
06f3b0b
enable immutability in ClientMethodMapper - phase4
anuchandy Apr 21, 2025
afc5d0f
enable immutability in ClientMethodMapper - phase5
anuchandy Apr 22, 2025
6f13078
enable immutability in ClientMethodMapper - phase6
anuchandy Apr 22, 2025
703683e
enable immutability in ClientMethodMapper - phase7
anuchandy Apr 22, 2025
4a3671f
enable immutability in ClientMethodMapper - phase8
anuchandy Apr 22, 2025
cc9519e
enable immutability in ClientMethodMapper - phase9
anuchandy Apr 22, 2025
9823d8a
enable immutability in ClientMethodMapper - phase10
anuchandy Apr 22, 2025
42626a5
Revert "enable immutability in ClientMethodMapper - phase10"
anuchandy Apr 22, 2025
bb9b5e7
Split lro begin
anuchandy Apr 22, 2025
38d6572
enable immutability in ClientMethodMapper - phase11
anuchandy Apr 22, 2025
e473a5f
enable immutability in ClientMethodMapper - phase11
anuchandy Apr 23, 2025
b4975aa
enable immutability in ClientMethodMapper - phase12
anuchandy Apr 23, 2025
bc6b9b5
java-doc to PagingMetadata, make continuation-token segement list imm…
anuchandy Apr 23, 2025
0908410
revert use of toImmutableList in PagingMetadata
anuchandy Apr 23, 2025
e6089d3
Renaming JavaSettings::PollingDetails to PollingSettings (so the name…
anuchandy Apr 24, 2025
487fd7d
move lro skip cases as a check within lro handling block
anuchandy Apr 24, 2025
564b79d
simplify some part of lro meta resolution
anuchandy Apr 24, 2025
eb873b6
simplify lro meta strategy resolution
anuchandy Apr 25, 2025
4985353
use consistent naming for poll intermediate-response/result and final…
anuchandy Apr 25, 2025
5165f88
Introducing PollingMetadata class LRO operations (similar to PagingMe…
anuchandy Apr 25, 2025
4bed082
ensure PollingSettings property names are consistent with MethodPolli…
anuchandy Apr 25, 2025
5736c92
additional javadoc for PollingMetadata
anuchandy Apr 25, 2025
f0eafd4
attempt lro condition simplification
anuchandy Apr 25, 2025
4cd34c3
initial attempt to use PollingMetadata
anuchandy Apr 25, 2025
4409806
polishing use of PollingMetadata
anuchandy Apr 26, 2025
906665e
Adding updated lro generated code since the method ordering changed
anuchandy Apr 26, 2025
fe3b555
Define CreateClientMethodArgs to pass immutable args across client me…
anuchandy Apr 26, 2025
8d836fc
move lro-with-response logic to new method createLroWithResponseMetho…
anuchandy Apr 27, 2025
edcf923
simplifying createLroWithResponseMethods by moving fluent specifics t…
anuchandy Apr 27, 2025
4c87f1f
simplifying createLroWithResponseMethods to take isSync param (like o…
anuchandy Apr 27, 2025
f708998
java doc, naming consistency
anuchandy Apr 27, 2025
0fca39b
make lro fluent conditional flow more explicit (will help when we do …
anuchandy Apr 27, 2025
39f44ef
Ensure FluentClientMethodMapper method creation follow the same codin…
anuchandy Apr 27, 2025
580ee6a
consolidate javaDoc setters, aligin paramaters ordering across create…
anuchandy Apr 28, 2025
c0b1d87
Updating based on first round of code review discussion
anuchandy Apr 28, 2025
92a5b38
consistent code pattern for method visibility
anuchandy Apr 28, 2025
6a4281e
incorporating review feedback (simplify createFluentLroWithResponseSy…
anuchandy Apr 29, 2025
afc912c
rebase with upstream/merging corev2 changes
anuchandy May 7, 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
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

import com.azure.json.JsonProviders;
import com.azure.json.JsonReader;
import com.azure.json.JsonSerializable;
import com.azure.json.JsonToken;
import com.azure.json.JsonWriter;
import com.microsoft.typespec.http.client.generator.core.mapper.Mappers;
import com.microsoft.typespec.http.client.generator.core.mapper.azurevnext.AzureVNextMapperFactory;
import com.microsoft.typespec.http.client.generator.core.mapper.clientcore.ClientCoreMapperFactory;
Expand Down Expand Up @@ -265,8 +263,8 @@ private JavaSettings(AutorestSettings autorestSettings) {
this.useIterable = getBooleanValue(host, "use-iterable", false);

// The versions of the service.
this.serviceVersions = host.getValueWithJsonReader("service-versions",
jsonReader -> jsonReader.readArray(JsonReader::getString));
this.serviceVersions
= host.getValueWithJsonReader("service-versions", reader -> reader.readArray(JsonReader::getString));

// The target for the <code>@JsonFlatten</code> annotation for x-ms-client-flatten.
String clientFlattenAnnotationTarget = getStringValue(host, "client-flattened-annotation-target", "");
Expand All @@ -282,14 +280,12 @@ private JavaSettings(AutorestSettings autorestSettings) {
this.clientBuilderDisabled = getBooleanValue(host, "disable-client-builder", false);

// The polling configuration.
Map<String, PollingDetails> pollingConfig
= host.getValueWithJsonReader("polling", jsonReader -> jsonReader.readMap(PollingDetails::fromJson));
if (pollingConfig != null) {
if (!pollingConfig.containsKey("default")) {
pollingConfig.put("default", new PollingDetails());
}
final Map<String, PollingSettings> operationPollingMapping
= host.getValueWithJsonReader("polling", reader -> reader.readMap(PollingSettings::fromJson));
if (operationPollingMapping != null && !operationPollingMapping.containsKey("default")) {
operationPollingMapping.put("default", new PollingSettings());
}
this.pollingConfig = pollingConfig;
this.operationPollingMapping = operationPollingMapping;

// Whether to generate samples.
this.generateSamples = getBooleanValue(host, "generate-samples", false);
Expand Down Expand Up @@ -1287,151 +1283,7 @@ public boolean isGenerateGraalVmConfig() {
return generateGraalVmConfig;
}

/**
* Represents the details of polling for a long-running operation.
*/
public static class PollingDetails implements JsonSerializable<PollingDetails> {
private String strategy;
private String syncStrategy;
private String intermediateType;
private String finalType;
private String pollInterval;

/**
* Creates a new PollingDetails object.
*/
public PollingDetails() {
}

/**
* The default polling strategy format.
*/
public static final String DEFAULT_POLLING_STRATEGY_FORMAT
= String.join("\n", "new %s<>(new PollingStrategyOptions({httpPipeline})", " .setEndpoint({endpoint})",
" .setContext({context})", " .setServiceVersion({serviceVersion}))");

public static final String DEFAULT_CLIENTCORE_POLLING_STRATEGY_FORMAT
= String.join("\n", "new %s<>(new PollingStrategyOptions({httpPipeline})", " .setEndpoint({endpoint})",
" .setRequestContext({context})", " .setServiceVersion({serviceVersion}))");

private static final String DEFAULT_POLLING_CODE
= String.format(DEFAULT_POLLING_STRATEGY_FORMAT, "DefaultPollingStrategy");

private static final String DEFAULT_SYNC_POLLING_CODE
= String.format(DEFAULT_POLLING_STRATEGY_FORMAT, "SyncDefaultPollingStrategy");

private static final String DEFAULT_CLIENTCORE_POLLING_CODE
= String.format(DEFAULT_CLIENTCORE_POLLING_STRATEGY_FORMAT, "DefaultPollingStrategy");

/**
* Gets the strategy for polling.
* <p>
* See the 'com.azure.core.util.polling.PollingStrategy' contract for more details.
* </p>
*
* @return The strategy for polling.
*/
public String getStrategy() {
if (strategy == null || "default".equalsIgnoreCase(strategy)) {
return DEFAULT_POLLING_CODE;
} else {
return strategy;
}
}

/**
* Gets the sync strategy for polling.
* <p>
* See the 'com.azure.core.util.polling.PollingStrategy' contract for more details.
* </p>
*
* @return The sync strategy for polling.
*/
public String getSyncStrategy() {
if (syncStrategy == null || "default".equalsIgnoreCase(syncStrategy)) {
if (JavaSettings.getInstance().isAzureV2()) {
return DEFAULT_CLIENTCORE_POLLING_CODE;
}
return DEFAULT_SYNC_POLLING_CODE;
} else {
return syncStrategy;
}
}

/**
* Gets the type of the poll response when the long-running operation is in progress.
*
* @return The intermediate type for polling.
*/
public String getIntermediateType() {
return intermediateType;
}

/**
* Gets the type of the poll response once the long-running operation is completed.
*
* @return The final type for polling.
*/
public String getFinalType() {
return finalType;
}

/**
* Gets the polling interval in seconds.
*
* @return The polling interval in seconds.
*/
public int getPollIntervalInSeconds() {
return pollInterval != null ? Integer.parseInt(pollInterval) : 1;
}

@Override
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
return jsonWriter.writeStartObject()
.writeStringField("strategy", strategy)
.writeStringField("sync-strategy", syncStrategy)
.writeStringField("intermediate-type", intermediateType)
.writeStringField("final-type", finalType)
.writeStringField("poll-interval", pollInterval)
.writeEndObject();
}

/**
* Deserializes a PollingDetails instance from the JSON data.
*
* @param jsonReader The JSON reader to deserialize from.
* @return A PollingDetails instance deserialized from the JSON data.
* @throws IOException If an error occurs during deserialization.
*/
public static PollingDetails fromJson(JsonReader jsonReader) throws IOException {
return jsonReader.readObject(reader -> {
PollingDetails pollingDetails = new PollingDetails();

while (reader.nextToken() != JsonToken.END_OBJECT) {
String fieldName = reader.getFieldName();
reader.nextToken();

if ("strategy".equals(fieldName)) {
pollingDetails.strategy = reader.getString();
} else if ("sync-strategy".equals(fieldName)) {
pollingDetails.syncStrategy = reader.getString();
} else if ("intermediate-type".equals(fieldName)) {
pollingDetails.intermediateType = reader.getString();
} else if ("final-type".equals(fieldName)) {
pollingDetails.finalType = reader.getString();
} else if ("poll-interval".equals(fieldName)) {
pollingDetails.pollInterval = reader.getString();
} else {
reader.skipChildren();
}
}

return pollingDetails;
});
}
}

private final Map<String, PollingDetails> pollingConfig;
private final Map<String, PollingSettings> operationPollingMapping;

/**
* Gets the polling configuration for the specified operation.
Expand All @@ -1440,16 +1292,16 @@ public static PollingDetails fromJson(JsonReader jsonReader) throws IOException
* @return The polling configuration for the specified operation, or the default polling configuration if no
* configuration is specified for the operation.
*/
public PollingDetails getPollingConfig(String operationId) {
if (pollingConfig == null) {
public PollingSettings getPollingSettings(String operationId) {
if (operationPollingMapping == null) {
return null;
}
for (String key : pollingConfig.keySet()) {
for (String key : operationPollingMapping.keySet()) {
if (key.equalsIgnoreCase(operationId)) {
return pollingConfig.get(key);
return operationPollingMapping.get(key);
}
}
return pollingConfig.get("default");
return operationPollingMapping.get("default");
}

private final boolean annotateGettersAndSettersForSerialization;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.typespec.http.client.generator.core.extension.plugin;

import com.azure.json.JsonReader;
import com.azure.json.JsonSerializable;
import com.azure.json.JsonToken;
import com.azure.json.JsonWriter;
import java.io.IOException;

/**
* type representing the user configured polling settings for long-running operations.
* <a href="https://github.com/Azure/autorest.java?tab=readme-ov-file#polling-configuration">Configure Polling
* Settings</a>
*/
public final class PollingSettings implements JsonSerializable<PollingSettings> {
private String pollingStrategy;
private String syncPollingStrategy;
private String pollResultType;
private String finalResultType;
private String pollInterval;

public PollingSettings() {
}

/**
* The format of the java source code to instantiate a polling strategy class with PollingStrategyOptions argument.
* For example, in case of the strategy 'OperationLocationPollingStrategy' class, the code after applying the format
* looks like -
* new OperationLocationPollingStrategy(new PollingStrategyOptions(...));
*/
public static final String INSTANTIATE_POLLING_STRATEGY_FORMAT;

/**
* The format of the java source code to instantiate a polling strategy class with PollingStrategyOptions and LRO
* final result arguments. For example, in case of the strategy 'OperationLocationPollingStrategy' class, the code
* after applying the format looks like -
* new OperationLocationPollingStrategy(new PollingStrategyOptions(...), finalResultType);
*/
public static final String INSTANTIATE_POLLING_STRATEGY_WITH_RESULT_FORMAT;
private static final String INSTANTIATE_DEFAULT_POLLING_STRATEGY;
private static final String INSTANTIATE_DEFAULT_SYNC_POLLING_STRATEGY;

static {
final String[] ctrOptionsArg = {
"new PollingStrategyOptions({httpPipeline})",
" .setEndpoint({endpoint})",
" .setContext({context})",
" .setServiceVersion({serviceVersion})" };
INSTANTIATE_POLLING_STRATEGY_FORMAT = "new %s<>" + "(" + String.join("\n", ctrOptionsArg) + ")";

final String[] ctrOptionsAndFinalResultArg = {
"new PollingStrategyOptions({httpPipeline})",
" .setEndpoint({endpoint})",
" .setContext({context})",
" .setServiceVersion({serviceVersion}), %s" };
INSTANTIATE_POLLING_STRATEGY_WITH_RESULT_FORMAT
= "new %s<>" + "(" + String.join("\n", ctrOptionsAndFinalResultArg) + ")";

INSTANTIATE_DEFAULT_POLLING_STRATEGY
= String.format(INSTANTIATE_POLLING_STRATEGY_FORMAT, "DefaultPollingStrategy");
INSTANTIATE_DEFAULT_SYNC_POLLING_STRATEGY
= String.format(INSTANTIATE_POLLING_STRATEGY_FORMAT, "SyncDefaultPollingStrategy");
}

public static final String DEFAULT_CLIENTCORE_POLLING_STRATEGY_FORMAT
= String.join("\n", "new %s<>(new PollingStrategyOptions({httpPipeline})", " .setEndpoint({endpoint})",
" .setRequestContext({context})", " .setServiceVersion({serviceVersion}))");

private static final String DEFAULT_CLIENTCORE_POLLING_CODE
= String.format(DEFAULT_CLIENTCORE_POLLING_STRATEGY_FORMAT, "DefaultPollingStrategy");

/**
* Gets the strategy for polling.
* <p>
* See the 'com.azure.core.util.polling.PollingStrategy' contract for more details.
* </p>
*
* @return The strategy for polling.
*/
public String getPollingStrategy() {
if (pollingStrategy == null || "default".equalsIgnoreCase(pollingStrategy)) {
return INSTANTIATE_DEFAULT_POLLING_STRATEGY;
} else {
return pollingStrategy;
}
}

/**
* Gets the sync strategy for polling.
* <p>
* See the 'com.azure.core.util.polling.PollingStrategy' contract for more details.
* </p>
*
* @return The sync strategy for polling.
*/
public String getSyncPollingStrategy() {
if (syncPollingStrategy == null || "default".equalsIgnoreCase(syncPollingStrategy)) {
if (JavaSettings.getInstance().isAzureV2()) {
return DEFAULT_CLIENTCORE_POLLING_CODE;
}
return INSTANTIATE_DEFAULT_SYNC_POLLING_STRATEGY;
} else {
return syncPollingStrategy;
}
}

/**
* Gets the type of the poll response when the long-running operation is in progress.
*
* @return The intermediate type for polling.
*/
public String getPollResultType() {
return pollResultType;
}

/**
* Gets the type of the poll response once the long-running operation is completed.
*
* @return The final type for polling.
*/
public String getFinalResultType() {
return finalResultType;
}

/**
* Gets the polling interval in seconds.
*
* @return The polling interval in seconds.
*/
public int getPollIntervalInSeconds() {
return pollInterval != null ? Integer.parseInt(pollInterval) : 1;
}

@Override
public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
return jsonWriter.writeStartObject()
.writeStringField("strategy", pollingStrategy)
.writeStringField("sync-strategy", syncPollingStrategy)
.writeStringField("intermediate-type", pollResultType)
.writeStringField("final-type", finalResultType)
.writeStringField("poll-interval", pollInterval)
.writeEndObject();
}

/**
* Deserializes a PollingSettings instance from the JSON data.
*
* @param jsonReader The JSON reader to deserialize from.
* @return A PollingSettings instance deserialized from the JSON data.
* @throws IOException If an error occurs during deserialization.
*/
public static PollingSettings fromJson(JsonReader jsonReader) throws IOException {
return jsonReader.readObject(reader -> {
final PollingSettings pollingSettings = new PollingSettings();

while (reader.nextToken() != JsonToken.END_OBJECT) {
String fieldName = reader.getFieldName();
reader.nextToken();

if ("strategy".equals(fieldName)) {
pollingSettings.pollingStrategy = reader.getString();
} else if ("sync-strategy".equals(fieldName)) {
pollingSettings.syncPollingStrategy = reader.getString();
} else if ("intermediate-type".equals(fieldName)) {
pollingSettings.pollResultType = reader.getString();
} else if ("final-type".equals(fieldName)) {
pollingSettings.finalResultType = reader.getString();
} else if ("poll-interval".equals(fieldName)) {
pollingSettings.pollInterval = reader.getString();
} else {
reader.skipChildren();
}
}

return pollingSettings;
});
}
}
Loading
Loading