io.rxmicro.annotation.processor.rest.client.RestClientModuleClassStructuresBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxmicro-annotation-processor-rest-client Show documentation
Show all versions of rxmicro-annotation-processor-rest-client Show documentation
The RxMicro Annotation Processor internal module that generates REST client components.
/*
* Copyright (c) 2020. https://rxmicro.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.rxmicro.annotation.processor.rest.client;
import com.google.inject.Inject;
import io.rxmicro.annotation.processor.common.CommonDependenciesModule;
import io.rxmicro.annotation.processor.common.FormatSourceCodeDependenciesModule;
import io.rxmicro.annotation.processor.common.component.ModuleGeneratorConfigBuilder;
import io.rxmicro.annotation.processor.common.component.ModuleInfoCustomizer;
import io.rxmicro.annotation.processor.common.component.impl.AbstractModuleClassStructuresBuilder;
import io.rxmicro.annotation.processor.common.model.ClassStructure;
import io.rxmicro.annotation.processor.common.model.EnvironmentContext;
import io.rxmicro.annotation.processor.common.model.ModuleInfoItem;
import io.rxmicro.annotation.processor.common.model.error.InterruptProcessingException;
import io.rxmicro.annotation.processor.common.model.virtual.VirtualTypeElement;
import io.rxmicro.annotation.processor.rest.RestCommonDependenciesModule;
import io.rxmicro.annotation.processor.rest.client.component.PathBuilderClassStructureBuilder;
import io.rxmicro.annotation.processor.rest.client.component.RequestModelExtractorClassStructureBuilder;
import io.rxmicro.annotation.processor.rest.client.component.RestClientClassSignatureBuilder;
import io.rxmicro.annotation.processor.rest.client.component.RestClientClassStructureBuilder;
import io.rxmicro.annotation.processor.rest.client.component.RestClientModelReaderBuilder;
import io.rxmicro.annotation.processor.rest.client.model.RestClientClassSignature;
import io.rxmicro.annotation.processor.rest.client.model.RestClientClassStructure;
import io.rxmicro.annotation.processor.rest.client.model.RestClientClassStructureStorage;
import io.rxmicro.annotation.processor.rest.client.model.RestClientFactoryClassStructure;
import io.rxmicro.annotation.processor.rest.client.model.RestClientMethodSignature;
import io.rxmicro.annotation.processor.rest.client.model.RestClientModuleGeneratorConfig;
import io.rxmicro.annotation.processor.rest.client.model.RestClientObjectModelClass;
import io.rxmicro.annotation.processor.rest.component.RestGenerationContextBuilder;
import io.rxmicro.annotation.processor.rest.component.RestModelFromJsonConverterBuilder;
import io.rxmicro.annotation.processor.rest.component.RestModelToJsonConverterBuilder;
import io.rxmicro.annotation.processor.rest.component.RestModelValidatorBuilder;
import io.rxmicro.annotation.processor.rest.model.MappedRestObjectModelClass;
import io.rxmicro.annotation.processor.rest.model.RestGenerationContext;
import io.rxmicro.annotation.processor.rest.model.VirtualTypeClassStructure;
import io.rxmicro.annotation.processor.rest.model.converter.ReaderType;
import io.rxmicro.rest.client.RestClient;
import io.rxmicro.rest.model.ExchangeFormat;
import io.rxmicro.validation.DisableValidation;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import static io.rxmicro.annotation.processor.common.util.Injects.injectDependencies;
import static io.rxmicro.annotation.processor.common.util.Names.getPackageName;
import static io.rxmicro.common.RxMicroModule.RX_MICRO_CONFIG_MODULE;
import static io.rxmicro.common.RxMicroModule.RX_MICRO_RUNTIME_MODULE;
import static io.rxmicro.common.util.Formats.format;
import static java.util.Map.entry;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
/**
* @author nedis
* @since 0.1
*/
public final class RestClientModuleClassStructuresBuilder extends AbstractModuleClassStructuresBuilder {
@Inject
private ModuleGeneratorConfigBuilder restClientModuleGeneratorConfigBuilder;
@Inject
private RestClientClassSignatureBuilder restClientClassSignatureBuilder;
@Inject
private RestClientClassStructureBuilder restClientClassStructureBuilder;
@Inject
private RestGenerationContextBuilder restGenerationContextBuilder;
@Inject
private RestClientModelReaderBuilder restClientModelReaderBuilder;
@Inject
private RestModelToJsonConverterBuilder restModelToJsonConverterBuilder;
@Inject
private RestModelFromJsonConverterBuilder restModelFromJsonConverterBuilder;
@Inject
private RequestModelExtractorClassStructureBuilder requestModelExtractorClassStructureBuilder;
@Inject
private PathBuilderClassStructureBuilder pathBuilderClassStructureBuilder;
@Inject
private RestModelValidatorBuilder restModelValidatorBuilder;
@Inject
private ModuleInfoCustomizer moduleInfoCustomizer;
public static RestClientModuleClassStructuresBuilder create() {
final RestClientModuleClassStructuresBuilder builder = new RestClientModuleClassStructuresBuilder();
injectDependencies(
builder,
new FormatSourceCodeDependenciesModule(),
new CommonDependenciesModule(),
new RestCommonDependenciesModule(),
new RestClientDependenciesModule()
);
return builder;
}
private RestClientModuleClassStructuresBuilder() {
}
@Override
public Set getSupportedAnnotationTypes() {
return Set.of(
RestClient.class.getName()
);
}
@Override
public Set extends ClassStructure> buildClassStructures(final EnvironmentContext environmentContext,
final Set extends TypeElement> annotations,
final RoundEnvironment roundEnv) {
try {
final Set classSignatures =
restClientClassSignatureBuilder.build(environmentContext, annotations, roundEnv);
if (!classSignatures.isEmpty()) {
environmentContext.put(restClientModuleGeneratorConfigBuilder.build(environmentContext));
logFoundRestClients(classSignatures);
final RestGenerationContext restGenerationContext =
restGenerationContextBuilder.build(environmentContext, RestClientModuleGeneratorConfig.class, classSignatures);
final RestClientClassStructureStorage restClientClassStructureStorage =
buildRestClientClassStructureStorage(environmentContext, classSignatures, restGenerationContext);
final Set classStructures = new HashSet<>(restClientClassStructureStorage.getAll());
final Set restClientClassStructures =
restClientClassStructureBuilder.build(environmentContext, restClientClassStructureStorage, classSignatures);
classStructures.addAll(restClientClassStructures);
final List moduleInfoItems =
buildModuleInfoItems(environmentContext, restClientClassStructures);
classStructures.add(new RestClientFactoryClassStructure(
restClientClassStructures,
restClientClassStructures.stream()
.flatMap(c -> c.getDefaultConfigValues().stream())
.collect(toList()),
moduleInfoItems
));
addAllVirtualRequestClassStructures(classStructures, classSignatures, restClientClassStructureStorage);
return classStructures;
}
return Set.of();
} catch (final InterruptProcessingException ex) {
error(ex);
return Set.of();
}
}
private void addAllVirtualRequestClassStructures(final Set classStructures,
final Set classSignatures,
final RestClientClassStructureStorage restClientClassStructureStorage) {
for (final RestClientClassSignature classSignature : classSignatures) {
for (final RestClientMethodSignature methodSignature : classSignature.getMethodSignatures()) {
methodSignature.getRequestModel().getRequestType()
.filter(t -> t instanceof VirtualTypeElement)
.flatMap(t -> restClientClassStructureStorage.getModelClass(t.getQualifiedName().toString()))
.ifPresent(modelClass -> classStructures.add(new VirtualTypeClassStructure(modelClass, true)));
}
}
}
private void logFoundRestClients(final Set set) {
info("Found the following rest clients:\n?", () -> set.stream()
.map(s -> s.getMethodSignatures().stream()
.map(e -> format(" ? ? -> ?",
e.getHttpMethodMapping().getMethod(),
e.getHttpMethodMapping().getExactOrTemplateUri(),
e.toString()))
.collect(joining("\n")))
.collect(joining("\n"))
);
}
private RestClientClassStructureStorage buildRestClientClassStructureStorage(final EnvironmentContext environmentContext,
final Set classSignatures,
final RestGenerationContext context) {
final ExchangeFormat clientExchangeFormat =
environmentContext.get(RestClientModuleGeneratorConfig.class).getExchangeFormatModule().getExchangeFormat();
final List toHttpQueryModelClasses = new ArrayList<>();
final List toHttpBodyModelClasses = new ArrayList<>();
final List toHttpPathModelClasses = new ArrayList<>();
separateModelClasses(context.getToHttpDataModelClasses(), toHttpQueryModelClasses, toHttpBodyModelClasses, toHttpPathModelClasses);
final RestClientClassStructureStorage.Builder builder = new RestClientClassStructureStorage.Builder()
.addModelReaders(
restClientModelReaderBuilder.build(
context.getFromHttpDataModelClasses(), classSignatures, clientExchangeFormat
)
)
.addModelFromJsonConverters(
restModelFromJsonConverterBuilder.buildFromJson(
context.getFromHttpDataModelClasses(), clientExchangeFormat, true
)
)
.addRequestModelExtractors(
requestModelExtractorClassStructureBuilder.build(toHttpQueryModelClasses)
)
.addPathBuilders(
pathBuilderClassStructureBuilder.build(toHttpPathModelClasses)
)
.addModelToJsonConverters(
restModelToJsonConverterBuilder.buildToJson(toHttpBodyModelClasses, clientExchangeFormat, true)
);
addValidators(environmentContext, context, builder);
builder.addRestObjectModelClasses(
context.getFromHttpDataModelClasses().stream().map(MappedRestObjectModelClass::getModelClass).collect(Collectors.toSet())
);
builder.addRestObjectModelClasses(
context.getToHttpDataModelClasses().stream().map(MappedRestObjectModelClass::getModelClass).collect(Collectors.toSet())
);
return builder.build();
}
private void separateModelClasses(final List toHttpDataModelClasses,
final List toHttpQueryModelClasses,
final List toHttpBodyModelClasses,
final List toHttpPathModelClasses) {
for (final MappedRestObjectModelClass modelClass : toHttpDataModelClasses) {
if (modelClass.getReaderType() == ReaderType.QUERY_STRING) {
if (modelClass.getModelClass().isParamsPresent() ||
modelClass.getModelClass().isHeadersPresent()) {
toHttpQueryModelClasses.add(modelClass);
}
} else if (modelClass.getReaderType() == ReaderType.HTTP_BODY) {
if (modelClass.getModelClass().isHeadersPresent()) {
toHttpQueryModelClasses.add(
modelClass.cloneUsingNewModelClass(
((RestClientObjectModelClass) modelClass.getModelClass()).cloneWithHeadersOnly()));
}
if (modelClass.getModelClass().isParamsPresent()) {
toHttpBodyModelClasses.add(modelClass);
}
} else if (modelClass.getModelClass().isParamsPresent() ||
modelClass.getModelClass().isHeadersPresent()) {
toHttpQueryModelClasses.add(modelClass);
toHttpBodyModelClasses.add(modelClass);
}
// path variables
if (modelClass.getModelClass().isPathVariablesPresent()) {
toHttpPathModelClasses.add(modelClass.cloneUsingNewModelClass(
((RestClientObjectModelClass) modelClass.getModelClass()).cloneWithPathVariablesOnly()));
}
}
}
private void addValidators(final EnvironmentContext environmentContext,
final RestGenerationContext context,
final RestClientClassStructureStorage.Builder builder) {
if (environmentContext.get(RestClientModuleGeneratorConfig.class).isGenerateRequestValidators()) {
builder.addRequestValidators(
restModelValidatorBuilder.build(context.getToHttpDataModelClasses().stream()
.map(MappedRestObjectModelClass::getModelClass)
.filter(m -> isAnnotationPerPackageHierarchyAbsent(
m.getModelTypeElement(), DisableValidation.class))
.collect(toList()))
);
}
if (environmentContext.get(RestClientModuleGeneratorConfig.class).isGenerateResponseValidators()) {
builder.addResponseValidators(
restModelValidatorBuilder.build(context.getFromHttpDataModelClasses().stream()
.map(MappedRestObjectModelClass::getModelClass)
.filter(m -> isAnnotationPerPackageHierarchyAbsent(
m.getModelTypeElement(), DisableValidation.class))
.collect(toList()))
);
}
}
private List buildModuleInfoItems(final EnvironmentContext environmentContext,
final Set restClientClassStructures) {
if (environmentContext.get(RestClientModuleGeneratorConfig.class).isGenerateRequiredModuleDirectives() &&
!environmentContext.getCurrentModule().isUnnamed()) {
final Set allModulePackages = environmentContext.get(RestClientModuleGeneratorConfig.class).getAllModulePackages();
final Set packages = restClientClassStructures.stream()
.map(r -> getPackageName(r.getHttpClientConfigFullClassName().asType()))
.filter(allModulePackages::contains)
.collect(Collectors.toSet());
return moduleInfoCustomizer.build(
environmentContext.getCurrentModule(),
packages.stream().map(p -> entry(p, RX_MICRO_RUNTIME_MODULE)).collect(toList()),
packages.stream().map(p -> entry(p, RX_MICRO_CONFIG_MODULE)).collect(toList())
);
} else {
return List.of();
}
}
}