Skip to content
Closed
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
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-bd762da.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add support for defining service model validators and generating valdiation reports during code generation."
}
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-f004fae.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add support for validating that shared models between two services are identical."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "Code Generator Maven Plugin",
"contributor": "",
"description": "Update the generator plugin to support model validation during code generation. In addition, this adds the `writeValidationReport` flag to support writing the validation report to disk."
}
5 changes: 5 additions & 0 deletions codegen-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
<groupId>software.amazon.awssdk</groupId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<artifactId>utils</artifactId>
<groupId>software.amazon.awssdk</groupId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
Expand All @@ -30,21 +34,23 @@
import org.apache.maven.project.MavenProject;
import software.amazon.awssdk.codegen.C2jModels;
import software.amazon.awssdk.codegen.CodeGenerator;
import software.amazon.awssdk.codegen.IntermediateModelBuilder;
import software.amazon.awssdk.codegen.internal.Utils;
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel;
import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel;
import software.amazon.awssdk.codegen.model.service.Paginators;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Waiters;
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils;
import software.amazon.awssdk.utils.StringUtils;

/**
* The Maven mojo to generate Java client code using software.amazon.awssdk:codegen module.
*/
@Mojo(name = "generate")
public class GenerationMojo extends AbstractMojo {

private static final String MODEL_FILE = "service-2.json";
private static final String CUSTOMIZATION_CONFIG_FILE = "customization.config";
private static final String WAITERS_FILE = "waiters-2.json";
Expand All @@ -62,6 +68,8 @@ public class GenerationMojo extends AbstractMojo {
@Parameter(property = "writeIntermediateModel", defaultValue = "false")
private boolean writeIntermediateModel;

@Parameter(property = "writeValidationReport", defaultValue = "false")
private boolean writeValidationReport;

@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
Expand All @@ -76,22 +84,59 @@ public void execute() throws MojoExecutionException {
this.resourcesDirectory = Paths.get(outputDirectory).resolve("generated-resources").resolve("sdk-resources");
this.testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests");

findModelRoots().forEach(p -> {
Path modelRootPath = p.modelRoot;
getLog().info("Loading from: " + modelRootPath.toString());
generateCode(C2jModels.builder()
.customizationConfig(p.customizationConfig)
.serviceModel(loadServiceModel(modelRootPath))
.waitersModel(loadWaiterModel(modelRootPath))
.paginatorsModel(loadPaginatorModel(modelRootPath))
.endpointRuleSetModel(loadEndpointRuleSetModel(modelRootPath))
.endpointTestSuiteModel(loadEndpointTestSuiteModel(modelRootPath))
.build());
List<GenerationParams> generationParams = initGenerationParams();

Map<String, IntermediateModel> serviceNameToModelMap = new HashMap<>();

generationParams.forEach(
params -> {
IntermediateModel model = params.intermediateModel;
String lowercaseServiceName = StringUtils.lowerCase(model.getMetadata().getServiceName());
IntermediateModel previous = serviceNameToModelMap.put(lowercaseServiceName, model);
if (previous != null) {
String warning = String.format("Multiple service models found with service name %s. Model validation "
+ "will likely be incorrect", lowercaseServiceName);
getLog().warn(warning);
}
});

// Update each param with the intermediate model it shares models with, if any
generationParams.forEach(params -> {
CustomizationConfig customizationConfig = params.intermediateModel.getCustomizationConfig();

if (customizationConfig.getShareModelConfig() != null) {
String shareModelWithName = customizationConfig.getShareModelConfig().getShareModelWith();
params.withShareModelsTarget(serviceNameToModelMap.get(shareModelWithName));
}
});

generationParams.forEach(this::generateCode);

project.addCompileSourceRoot(sourcesDirectory.toFile().getAbsolutePath());
project.addTestCompileSourceRoot(testsDirectory.toFile().getAbsolutePath());
}

private List<GenerationParams> initGenerationParams() throws MojoExecutionException {
List<ModelRoot> modelRoots = findModelRoots().collect(Collectors.toList());

return modelRoots.stream().map(r -> {
Path modelRootPath = r.modelRoot;
getLog().info("Loading from: " + modelRootPath.toString());
C2jModels c2jModels = C2jModels.builder()
.customizationConfig(r.customizationConfig)
.serviceModel(loadServiceModel(modelRootPath))
.waitersModel(loadWaiterModel(modelRootPath))
.paginatorsModel(loadPaginatorModel(modelRootPath))
.endpointRuleSetModel(loadEndpointRuleSetModel(modelRootPath))
.endpointTestSuiteModel(loadEndpointTestSuiteModel(modelRootPath))
.build();
String intermediateModelFileNamePrefix = intermediateModelFileNamePrefix(c2jModels);
IntermediateModel intermediateModel = new IntermediateModelBuilder(c2jModels).build();
return new GenerationParams().withIntermediateModel(intermediateModel)
.withIntermediateModelFileNamePrefix(intermediateModelFileNamePrefix);
}).collect(Collectors.toList());
}

private Stream<ModelRoot> findModelRoots() throws MojoExecutionException {
try {
return Files.find(codeGenResources.toPath(), 10, this::isModelFile)
Expand All @@ -111,13 +156,15 @@ private boolean isModelFile(Path p, BasicFileAttributes a) {
return p.toString().endsWith(MODEL_FILE);
}

private void generateCode(C2jModels models) {
private void generateCode(GenerationParams params) {
CodeGenerator.builder()
.models(models)
.intermediateModel(params.intermediateModel)
.shareModelsTarget(params.shareModelsTarget)
.sourcesDirectory(sourcesDirectory.toFile().getAbsolutePath())
.resourcesDirectory(resourcesDirectory.toFile().getAbsolutePath())
.testsDirectory(testsDirectory.toFile().getAbsolutePath())
.intermediateModelFileNamePrefix(intermediateModelFileNamePrefix(models))
.intermediateModelFileNamePrefix(params.intermediateModelFileNamePrefix)
.emitValidationReport(writeValidationReport)
.build()
.execute();
}
Expand Down Expand Up @@ -178,4 +225,25 @@ private ModelRoot(Path modelRoot, CustomizationConfig customizationConfig) {
this.customizationConfig = customizationConfig;
}
}

private static class GenerationParams {
private IntermediateModel intermediateModel;
private IntermediateModel shareModelsTarget;
private String intermediateModelFileNamePrefix;

public GenerationParams withIntermediateModel(IntermediateModel intermediateModel) {
this.intermediateModel = intermediateModel;
return this;
}

public GenerationParams withShareModelsTarget(IntermediateModel shareModelsTarget) {
this.shareModelsTarget = shareModelsTarget;
return this;
}

public GenerationParams withIntermediateModelFileNamePrefix(String intermediateModelFileNamePrefix) {
this.intermediateModelFileNamePrefix = intermediateModelFileNamePrefix;
return this;
}
}
}
5 changes: 5 additions & 0 deletions codegen/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,10 @@
<artifactId>mockito-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Loading
Loading