Init commit

This commit is contained in:
Artur Kruszewski
2026-05-17 21:24:07 +02:00
commit 2e71707b79
14 changed files with 923 additions and 0 deletions
@@ -0,0 +1,125 @@
package com.krusoe.openapimerge.merge;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.PathItem;
import org.apache.commons.io.FileUtils;
import org.openapitools.codegen.validation.Invalid;
import org.openapitools.codegen.validation.ValidationRule;
import org.openapitools.codegen.validations.oas.OpenApiEvaluator;
import org.openapitools.codegen.validations.oas.RuleConfiguration;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
@Command
public class MergeCommand {
private ObjectMapper objectMapper = new ObjectMapper();
@Command(description = "Merge APIs")
public void merge(@Option(required = true, shortNames = {'d'}, longNames = {"dir"}) String dir, @Option(required = false, shortNames = {'o'}, longNames = {"out"}) String out) throws IOException {
File dirFile = new File(dir);
if (FileUtils.isDirectory(dirFile)) {
ApiSpec mergedSpec = new ApiSpec();
for (File processedFile : FileUtils.listFiles(dirFile, new String[]{".json"}, true)) {
System.out.println("Processing file: " + processedFile.getAbsolutePath());
ApiSpec currentFileSpec = objectMapper.readerFor(ApiSpec.class).readValue(processedFile);
mergedSpec.merge(currentFileSpec);
}
var outFile = new File(dirFile, "mergedapi.json");
if (outFile.exists()) {
outFile.delete();
}
System.out.println("Writing merged APIs");
try (OutputStream outStream = FileUtils.newOutputStream(outFile, false)) {
objectMapper.writerWithDefaultPrettyPrinter().writeValue(outStream, mergedSpec);
}
}
}
@Command(description = "Validate APIs")
public void validate(@Option(required = true, shortNames = {'f'}, longNames = {"file"}) String fileToBalidate) throws IOException {
File outFile = new File(fileToBalidate);
if (outFile.isDirectory()) {
System.out.println("Given file is an directory");
} else {
validateFile(outFile);
}
}
private void validateFile(File outFile) throws IOException {
if (outFile.exists()) {
System.out.println("Validating OpenAPI file " + outFile.getAbsolutePath());
OpenApiEvaluator evaluator = new OpenApiEvaluator(new RuleConfiguration());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OpenAPI validatedApi = Json.mapper().readValue(outFile, OpenAPI.class);
var validationResult = evaluator.validate(validatedApi);
Set<String> uniqueOperationIds = new HashSet<>();
Set<String> nonUniqueOperationIds = new HashSet<>();
for (PathItem processedItem : validatedApi.getPaths().sequencedValues()) {
Stream.of(processedItem.getDelete(),
processedItem.getGet(),
processedItem.getPost(),
processedItem.getPut(),
processedItem.getPut(),
processedItem.getPatch(),
processedItem.getHead(),
processedItem.getOptions(),
processedItem.getTrace())
.filter(Objects::nonNull)
.forEach(
operation -> {
if (uniqueOperationIds.contains(operation.getOperationId())) {
validationResult.getErrors()
.add(
new Invalid(
ValidationRule.error("Non unique operationId", o -> null),
"Non unique operationId",
"Path: " + processedItem.get$ref() + " Operation:" + operation.getOperationId()));
nonUniqueOperationIds.add(operation.getOperationId());
uniqueOperationIds.add(operation.getOperationId());
} else {
uniqueOperationIds.add(operation.getOperationId());
}
}
);
}
if (validationResult.getErrors().isEmpty() && validationResult.getWarnings().isEmpty()) {
System.out.println("Validation complete. No validation errors or warning found. File is valid.");
} else {
System.out.println("Validation errors:");
validationResult.getErrors().forEach(System.out::println);
System.out.println("Validation warnings:");
validationResult.getWarnings().forEach(w -> System.out.println("Severity: " + w.getSeverity().name() + "\nRule: " + w.getRule().getDescription() + "\nMessage:\n" + w.getMessage() + "\nDetails:" + w.getDetails()));
}
}
}
}