io.github.itroadlabs.apicross.springmvc.SpringMvcCodeGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apicross-springmvc Show documentation
Show all versions of apicross-springmvc Show documentation
Spring Web MVC code generator
The newest version!
package io.github.itroadlabs.apicross.springmvc;
import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.io.ClassPathTemplateLoader;
import io.github.itroadlabs.apicross.core.data.model.DataModel;
import io.github.itroadlabs.apicross.core.data.model.ObjectDataModel;
import io.github.itroadlabs.apicross.core.data.model.PrimitiveDataModel;
import io.github.itroadlabs.apicross.core.handler.model.MediaTypeContentModel;
import io.github.itroadlabs.apicross.core.handler.model.RequestQueryParameter;
import io.github.itroadlabs.apicross.core.handler.model.RequestsHandler;
import io.github.itroadlabs.apicross.core.handler.model.RequestsHandlerMethod;
import io.github.itroadlabs.apicross.java.JavaCodeGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Slf4j
public class SpringMvcCodeGenerator extends JavaCodeGenerator {
private Template requestsHandlerQueryObjectTemplate;
private Template dataModelReadInterfaceTemplate;
private boolean enableApicrossJavaBeanValidationSupport = false;
private boolean enableDataModelReadInterfaces = false;
private boolean enableSpringSecurityAuthPrincipal = false;
private boolean useQueryStringParametersObject = true;
private String apiModelReadInterfacesPackage;
@Override
public void setOptions(SpringMvcCodeGeneratorOptions options) throws Exception {
super.setOptions(options);
this.enableApicrossJavaBeanValidationSupport = options.isEnableApicrossJavaBeanValidationSupport();
this.enableDataModelReadInterfaces = options.isEnableDataModelReadInterfaces();
this.apiModelReadInterfacesPackage = options.getApiModelReadInterfacesPackage();
this.enableSpringSecurityAuthPrincipal = options.isEnableSpringSecurityAuthPrincipal();
if (this.apiModelReadInterfacesPackage == null) {
this.apiModelReadInterfacesPackage = super.apiModelPackage;
}
this.useQueryStringParametersObject = options.isUseQueryStringParametersObject();
}
@Override
protected void initSourceCodeTemplates(Handlebars templatesEngine) throws IOException {
super.initSourceCodeTemplates(templatesEngine);
this.requestsHandlerQueryObjectTemplate = templatesEngine.compile("requestsHandlerQueryStringParametersObject");
this.dataModelReadInterfaceTemplate = templatesEngine.compile("dataModelReadInterface");
templatesEngine.setInfiniteLoops(true); // for recursion with type.hbs
}
@Override
protected void preProcess(Iterable models, Iterable handlers) {
super.preProcess(models, handlers);
if (!getOptions().isGenerateOnlyModels()) {
handleNeedToUseRequestBodyAnnotation(handlers);
}
if (enableSpringSecurityAuthPrincipal) {
processSpringSecurityAuthPrincipalFeature(handlers);
}
}
protected void processSpringSecurityAuthPrincipalFeature(Iterable handlers) {
for (RequestsHandler handler : handlers) {
List methods = handler.getMethods();
boolean anySecurityOptionsForHandler = false;
for (RequestsHandlerMethod method : methods) {
if (method.hasSecurityOptions()) {
method.addCustomAttribute("addAuthPrincipalArg", Boolean.TRUE);
anySecurityOptionsForHandler = true;
}
}
if (anySecurityOptionsForHandler) {
handler.addCustomAttribute("enableSpringSecurityAuthPrincipal", Boolean.TRUE);
}
}
}
@Override
protected List prepareJavaClassDataModels(Collection schemas, Collection handlers) {
final List objectDataModels = super.prepareJavaClassDataModels(schemas, handlers);
if (enableDataModelReadInterfaces) {
for (ObjectDataModel model : objectDataModels) {
Map customAttributes = model.getCustomAttributes();
List ifaces = (List) customAttributes.get("implementsInterfaces");
if (ifaces == null) {
ifaces = new ArrayList<>();
model.addCustomAttribute("implementsInterfaces", ifaces);
}
String iface = "IRead" + model.getOriginalTypeName();
ifaces.add(iface);
if (!super.apiModelPackage.equals(apiModelReadInterfacesPackage)) {
model.addCustomAttribute("apiModelReadInterfacesPackage", apiModelReadInterfacesPackage);
}
}
}
return objectDataModels;
}
protected void handleNeedToUseRequestBodyAnnotation(Iterable handlers) {
handlers.forEach(requestsHandler -> requestsHandler.getMethods().forEach(requestsHandlerMethod -> {
MediaTypeContentModel requestBody = requestsHandlerMethod.getRequestBody();
if (requestBody != null) {
if ("multipart/form-data".equals(requestBody.getMediaType()) ||
"application/x-www-form-urlencoded".equals(requestBody.getMediaType())) {
requestBody.addCustomAttribute("avoidRequestBodyAnnotation", Boolean.TRUE);
} else {
// this is for cases like this:
// --------------------------------
// POST /my-resource
// Content-Type: image/png
// --------------------------------
// ...when API specification declares binary content in the request body
DataModel content = requestBody.getContent();
if ((content instanceof PrimitiveDataModel)) {
PrimitiveDataModel primitiveDataModel = (PrimitiveDataModel) content;
String type = primitiveDataModel.getType();
String format = primitiveDataModel.getFormat();
if ("string".equals(type) && "binary".equals(format)) {
requestBody.addCustomAttribute("avoidRequestBodyAnnotation", Boolean.TRUE);
}
}
}
}
}));
}
@Override
protected ClassPathTemplateLoader setupDefaultTemplatesLoader() {
return new ClassPathTemplateLoader("/io/github/itroadlabs/apicross/springmvc/templates", ".hbs");
}
@Override
protected void writeHandlersSources(File handlersPackageDir, File modelsPackageDir, List handlers) throws IOException {
super.writeHandlersSources(handlersPackageDir, modelsPackageDir, handlers);
if (this.useQueryStringParametersObject) {
writeApiHandlerQueryObjectModels(handlers, modelsPackageDir);
}
}
@Override
protected void writeModelsSources(File modelsPackageDir, List models) throws IOException {
super.writeModelsSources(modelsPackageDir, models);
if (enableDataModelReadInterfaces) {
File apiModelReadInterfacesDirectory = new File(getWriteSourcesTo(), toFilePath(apiModelReadInterfacesPackage));
apiModelReadInterfacesDirectory.mkdirs();
writeDataModelsReadInterfaceSourceFiles(models, apiModelReadInterfacesDirectory, model -> "IRead" + model.getOriginalTypeName() + ".java");
}
}
protected void writeApiHandlerQueryObjectModels(List handlers, File modelsPackageDir) throws IOException {
log.info("Writing QueryObject data models...");
Set handledOperations = new LinkedHashSet<>(); // 1 query object for operationId
for (RequestsHandler handler : handlers) {
List methods = handler.getMethods();
for (RequestsHandlerMethod method : methods) {
String operationId = method.getOperationId();
if (method.hasQueryParameters() && !handledOperations.contains(operationId)) {
Collection queryParameters = method.getQueryParameters();
for (RequestQueryParameter queryParameter : queryParameters) {
DataModel queryParameterType = queryParameter.getType();
setupDataModelConstraintsCustomAttributes(queryParameterType, new HashSet<>());
}
File sourceFile = new File(modelsPackageDir, StringUtils.capitalize(operationId) + "Query.java");
try (FileOutputStream out = new FileOutputStream(sourceFile)) {
PrintWriter sourcePrintWriter = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
String iface = queryObjectsInterfacesMap != null ? queryObjectsInterfacesMap.get(operationId) : null;
Set ifaces = new LinkedHashSet<>();
if (iface != null) {
ifaces.add(iface);
}
if (globalQueryObjectsInterfaces != null && !globalQueryObjectsInterfaces.isEmpty()) {
ifaces.addAll(globalQueryObjectsInterfaces);
}
Context context = buildTemplateContext(method, apiModelPackage);
if (!ifaces.isEmpty()) {
context.combine("queryObjectTypeInterfaces", ifaces);
}
context.combine("queryObjectRequiredProperties", queryParameters.stream()
.filter(RequestQueryParameter::isRequired)
.map(RequestQueryParameter::getName)
.collect(Collectors.toSet()));
writeSource(sourcePrintWriter, requestsHandlerQueryObjectTemplate.apply(context));
}
handledOperations.add(operationId);
}
}
}
}
protected void writeDataModelsReadInterfaceSourceFiles(List models, File modelsPackageDir, Function fileNameFactory) throws IOException {
log.info("Writing API data models read interfaces...");
for (ObjectDataModel model : models) {
File sourceFile = new File(modelsPackageDir, fileNameFactory.apply(model));
try (FileOutputStream out = new FileOutputStream(sourceFile)) {
PrintWriter sourcePrintWriter = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
Context context = buildTemplateContext(model, apiModelReadInterfacesPackage);
writeSource(sourcePrintWriter, dataModelReadInterfaceTemplate.apply(context));
}
}
}
@Override
protected Map buildGeneratorOpts() {
Map generatorOpts = super.buildGeneratorOpts();
generatorOpts.put("enableApicrossJavaBeanValidationSupport", this.enableApicrossJavaBeanValidationSupport);
generatorOpts.put("useQueryStringParametersObject", this.useQueryStringParametersObject);
return generatorOpts;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy