Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
changeKind: internal
packages:
- "@typespec/http-client-java"
---

Simplify the mapper class hierarchy by consolidating the TypeSpec-specific mappers into
the shared base mappers.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import com.microsoft.typespec.http.client.generator.core.template.Templates;
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.ModelUtil;
import com.microsoft.typespec.http.client.generator.core.util.SchemaUtil;
import io.clientcore.core.utils.CoreUtils;
import java.util.ArrayList;
Expand Down Expand Up @@ -481,7 +482,16 @@ private void addProtocolExamples(Client.Builder builder, List<AsyncSyncClient> s
protected Map<ServiceClient, com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Client>
processClients(List<com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Client> clients,
CodeModel codeModel) {
return Map.of();
Map<ServiceClient, com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Client> serviceClientsMap
= new LinkedHashMap<>();
ServiceClientMapper mapper = new ServiceClientMapper();
for (com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Client client : clients) {
ServiceClient serviceClient = mapper.map(client, codeModel);
if (serviceClient != null) {
serviceClientsMap.put(serviceClient, client);
}
}
return serviceClientsMap;
}

private void addBuilderTraits(ClientBuilder clientBuilder, ServiceClient serviceClient) {
Expand Down Expand Up @@ -615,9 +625,19 @@ public ObjectSchema parseHeader(Operation operation, JavaSettings settings) {
}

protected String getResponseHeaderName(Header header) {
// We should use header.getLanguage().getDefault().getName()
// kept as header.getHeader() for backward compatibility
return header.getHeader();
String clientName;
if (header.getLanguage() != null
&& header.getLanguage().getJava() != null
&& !CoreUtils.isNullOrEmpty(header.getLanguage().getJava().getName())) {
clientName = header.getLanguage().getJava().getName();
} else if (header.getLanguage() != null
&& header.getLanguage().getDefault() != null
&& !CoreUtils.isNullOrEmpty(header.getLanguage().getDefault().getName())) {
clientName = header.getLanguage().getDefault().getName();
} else {
clientName = header.getHeader();
}
return clientName;
}

private ClientResponse parseResponse(Operation method, List<ClientModel> models, JavaSettings settings) {
Expand Down Expand Up @@ -693,26 +713,16 @@ private static ModuleInfo getModuleInfo(List<String> modelsPackages, Collection<
*/
protected List<String> getModelsPackages(List<ClientModel> clientModels, List<EnumType> enumTypes,
List<ClientResponse> responseModels) {
Set<String> packages = new LinkedHashSet<>();

List<String> ret = List.of();

JavaSettings settings = JavaSettings.getInstance();
boolean hasModels = !settings.isDataPlaneClient() // not DPG
// defined models package (it is defined by default)
&& (settings.getModelsSubpackage() != null && !settings.getModelsSubpackage().isEmpty())
// models package is not same as implementation package
&& !settings.getModelsSubpackage().equals(settings.getImplementationSubpackage());

if (hasModels) {
Set<String> packages = clientModels.stream().map(ClientModel::getPackage).collect(Collectors.toSet());

packages.addAll(enumTypes.stream().map(EnumType::getPackage).collect(Collectors.toSet()));
packages.addAll(responseModels.stream().map(ClientResponse::getPackage).collect(Collectors.toSet()));

ret = new ArrayList<>(packages);
}
clientModels.stream().filter(ModelUtil::isGeneratingModel).map(ClientModel::getPackage).forEach(packages::add);
enumTypes.stream().filter(ModelUtil::isGeneratingModel).map(EnumType::getPackage).forEach(packages::add);
responseModels.stream()
.filter(ModelUtil::isGeneratingModel)
.map(ClientResponse::getPackage)
.forEach(packages::add);

return ret;
return new ArrayList<>(packages);
}

public static ClassType getClientResponseClassType(Operation method, List<ClientModel> models,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected IType createPrimitiveType(PrimitiveSchema primaryType) {
return PrimitiveType.CHAR;

case DATE:
return isLowLevelClient ? ClassType.STRING : ClassType.LOCAL_DATE;
return ClassType.LOCAL_DATE;

case DATE_TIME:
DateTimeSchema dateTimeSchema = (DateTimeSchema) primaryType;
Expand Down Expand Up @@ -127,7 +127,7 @@ protected IType createPrimitiveType(PrimitiveSchema primaryType) {
return durationType;

case UNIXTIME:
return isLowLevelClient ? PrimitiveType.LONG : PrimitiveType.UNIX_TIME_LONG;
return PrimitiveType.UNIX_TIME_LONG;

case UUID:
return isLowLevelClient || uuidAsString ? ClassType.STRING : ClassType.UUID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ArrayType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClassType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.EnumType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.IType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.IterableType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ListType;
Expand Down Expand Up @@ -185,6 +186,8 @@ public ProxyMethodParameter map(Parameter parameter) {
}

protected boolean isRemoveModelFromParameter(Parameter parameter, IType clientType) {
return JavaSettings.getInstance().isDataPlaneClient();
boolean isEnumType = clientType instanceof EnumType;
boolean isClientParameter = Parameter.ImplementationLocation.CLIENT.equals(parameter.getImplementation());
return JavaSettings.getInstance().isDataPlaneClient() && !(isEnumType && isClientParameter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.Scheme;
import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClassType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClientAccessorMethod;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClientMethod;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClientMethodParameter;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.Constructor;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.EnumType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.IType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.MethodGroupClient;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ParameterSynthesizedOrigin;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.PipelinePolicyDetails;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.Proxy;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ProxyMethod;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.SecurityInfo;
Expand All @@ -32,9 +35,12 @@
import io.clientcore.core.utils.CoreUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -45,6 +51,8 @@ public class ServiceClientMapper implements IMapper<CodeModel, ServiceClient> {
private static final Pattern TRAILING_FORWARD_SLASH = Pattern.compile("/+$");
private static final Pattern URL_PATH = Pattern.compile("(?<!/)[/][^/]+");

private final Map<Client, ServiceClient> parsed = new ConcurrentHashMap<>();

protected ServiceClientMapper() {
}

Expand Down Expand Up @@ -250,8 +258,108 @@ protected List<ServiceClientProperty> processClientProperties(Client client, Str
return serviceClientProperties;
}

/**
* Maps a single TypeSpec client to a service client.
*
* @param client the code model client.
* @param codeModel the code model.
* @return the service client.
*/
public ServiceClient map(Client client, CodeModel codeModel) {
if (parsed.containsKey(client)) {
return parsed.get(client);
}

ServiceClient.Builder builder = createClientBuilder();

String baseName = SchemaUtil.getJavaName(client);
String className = ClientModelUtil.getClientImplementClassName(baseName);
String packageName = ClientModelUtil.getServiceClientPackageName(className);
builder.interfaceName(baseName).className(className).packageName(packageName);
if (client.getLanguage().getJava() != null
&& !CoreUtils.isNullOrEmpty(client.getLanguage().getJava().getNamespace())) {
builder.builderPackageName(client.getLanguage().getJava().getNamespace());
}

builder.builderDisabled(!client.isBuildMethodPublic());

Proxy proxy = null;
OperationGroup clientOperationGroup = client.getOperationGroups()
.stream()
.filter(og -> CoreUtils.isNullOrEmpty(SchemaUtil.getJavaName(og)))
.findFirst()
.orElse(null);
if (clientOperationGroup != null) {
proxy = processClientOperations(builder, clientOperationGroup.getOperations(), baseName);
} else {
builder.clientMethods(Collections.emptyList());
}

List<ServiceClientProperty> properties = processClientProperties(client,
client.getServiceVersion() == null ? null : client.getServiceVersion().getLanguage().getJava().getName());

List<MethodGroupClient> methodGroupClients = new ArrayList<>();
client.getOperationGroups()
.stream()
.filter(og -> !CoreUtils.isNullOrEmpty(SchemaUtil.getJavaName(og)))
.forEach(og -> methodGroupClients.add(Mappers.getMethodGroupMapper().map(og, properties)));
builder.methodGroupClients(methodGroupClients);

if (proxy == null
&& CoreUtils.isNullOrEmpty(methodGroupClients)
&& CoreUtils.isNullOrEmpty(client.getSubClients())) {
// No operation in this client, no operation group, no sub client as well. Abort the processing.
return null;
}

if (proxy == null && !CoreUtils.isNullOrEmpty(methodGroupClients)) {
proxy = methodGroupClients.iterator().next().getProxy();
}

// TODO (weidxu): security definition could be different for different client
processParametersAndConstructors(builder, client, codeModel, properties, proxy);

processPipelinePolicyDetails(builder, client);

builder.crossLanguageDefinitionId(SchemaUtil.getCrossLanguageDefinitionId(client));

List<ClientAccessorMethod> clientAccessorMethods = new ArrayList<>();
for (Client subClient : client.getSubClients()) {
if (subClient.isParentAccessorPublic()) {
ServiceClient subServiceClient = this.map(subClient, codeModel);
clientAccessorMethods.add(new ClientAccessorMethod(subServiceClient));
}
}
builder.clientAccessorMethods(clientAccessorMethods);

ServiceClient serviceClient = builder.build();

clientAccessorMethods.forEach(m -> {
m.setServiceClient(serviceClient);
m.getSubClient().setParentClient(serviceClient);
});

parsed.put(client, serviceClient);
return serviceClient;
}

private static void processPipelinePolicyDetails(ServiceClient.Builder builder, Client client) {
// handle corner case of RequestIdPolicy with header name "client-request-id"
final String clientRequestIdHeaderName = "client-request-id";
final boolean clientRequestIdHeaderInClient = client.getOperationGroups()
.stream()
.flatMap(og -> og.getOperations().stream())
.anyMatch(o -> o.getSpecialHeaders() != null && o.getSpecialHeaders().contains(clientRequestIdHeaderName));
if (clientRequestIdHeaderInClient) {
builder
.pipelinePolicyDetails(new PipelinePolicyDetails().setRequestIdHeaderName(clientRequestIdHeaderName));
}
}

protected boolean isRemoveModelFromParameter(Parameter parameter, IType type) {
return JavaSettings.getInstance().isDataPlaneClient();
boolean isEnumType = type instanceof EnumType;
boolean isClientParameter = Parameter.ImplementationLocation.CLIENT.equals(parameter.getImplementation());
return JavaSettings.getInstance().isDataPlaneClient() && !(isEnumType && isClientParameter);
}

protected void processParametersAndConstructors(ServiceClient.Builder builder, Client client, CodeModel codeModel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.typespec.http.client.generator.util;
package com.microsoft.typespec.http.client.generator.core.util;

import com.microsoft.typespec.http.client.generator.core.extension.plugin.JavaSettings;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClassType;
Expand All @@ -11,7 +11,6 @@
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.IType;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ImplementationDetails;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.UnionModel;
import com.microsoft.typespec.http.client.generator.core.util.ClientModelUtil;

public final class ModelUtil {

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@

package com.microsoft.typespec.http.client.generator.mgmt.mapper;

import com.microsoft.typespec.http.client.generator.core.mapper.ChoiceMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.ClientMethodMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.DefaultMapperFactory;
import com.microsoft.typespec.http.client.generator.core.mapper.ExceptionMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.MethodGroupMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.ModelMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.ModelPropertyMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.ObjectMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.PrimitiveMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.ProxyMethodMapper;
import com.microsoft.typespec.http.client.generator.core.mapper.SealedChoiceMapper;

public class FluentMapperFactory extends DefaultMapperFactory {

Expand Down Expand Up @@ -41,23 +39,13 @@ public ClientMethodMapper getClientMethodMapper() {
return FluentClientMethodMapper.getInstance();
}

@Override
public PrimitiveMapper getPrimitiveMapper() {
return FluentPrimitiveMapper.getInstance();
}

@Override
public ModelMapper getModelMapper() {
return FluentModelMapper.getInstance();
}

@Override
public ChoiceMapper getChoiceMapper() {
return FluentChoiceMapper.getInstance();
}

@Override
public SealedChoiceMapper getSealedChoiceMapper() {
return FluentSealedChoiceMapper.getInstance();
public ModelPropertyMapper getModelPropertyMapper() {
return FluentModelPropertyMapper.getInstance();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.microsoft.typespec.http.client.generator.mgmt.mapper;

import com.microsoft.typespec.http.client.generator.core.extension.model.codemodel.ObjectSchema;
import com.microsoft.typespec.http.client.generator.core.mapper.ModelMapper;
import com.microsoft.typespec.http.client.generator.core.model.clientmodel.ClassType;
import com.microsoft.typespec.http.client.generator.mgmt.model.FluentType;
Expand All @@ -19,6 +20,11 @@ public static FluentModelMapper getInstance() {
return INSTANCE;
}

@Override
public boolean isPlainObject(ObjectSchema compositeType) {
return false;
}

@Override
protected boolean isPredefinedModel(ClassType modelType) {
return !FluentType.nonResourceType(modelType)
Expand Down
Loading
Loading