Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix renderer memory issues by writing to OutputStream #544

Merged
merged 1 commit into from
Jul 25, 2023
Merged
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
46 changes: 19 additions & 27 deletions cli/src/main/java/org/openapitools/openapidiff/cli/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import ch.qos.logback.classic.Level;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.List;
import org.apache.commons.cli.CommandLine;
Expand All @@ -14,7 +14,6 @@
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.openapitools.openapidiff.core.OpenApiCompare;
import org.openapitools.openapidiff.core.model.ChangedOpenApi;
Expand Down Expand Up @@ -175,29 +174,33 @@ public static void main(String... args) {
ChangedOpenApi result = OpenApiCompare.fromLocations(oldPath, newPath, auths);
ConsoleRender consoleRender = new ConsoleRender();
if (!logLevel.equals("OFF")) {
System.out.println(consoleRender.render(result));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
consoleRender.render(result, outputStreamWriter);
System.out.println(outputStream);
}
if (line.hasOption("html")) {
HtmlRender htmlRender = new HtmlRender();
String output = htmlRender.render(result);
String outputFile = line.getOptionValue("html");
writeOutput(output, outputFile);
FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("html"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
htmlRender.render(result, outputStreamWriter);
}
if (line.hasOption("markdown")) {
MarkdownRender mdRender = new MarkdownRender();
String output = mdRender.render(result);
String outputFile = line.getOptionValue("markdown");
writeOutput(output, outputFile);
FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("markdown"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
mdRender.render(result, outputStreamWriter);
}
if (line.hasOption("text")) {
String output = consoleRender.render(result);
String outputFile = line.getOptionValue("text");
writeOutput(output, outputFile);
FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("text"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
consoleRender.render(result, outputStreamWriter);
}
if (line.hasOption("json")) {
JsonRender jsonRender = new JsonRender();
String outputFile = line.getOptionValue("json");
jsonRender.renderToFile(result, outputFile);
FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("json"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
jsonRender.render(result, outputStreamWriter);
}
if (line.hasOption("state")) {
System.out.println(result.isChanged().getValue());
Expand All @@ -222,17 +225,6 @@ public static void main(String... args) {
}
}

private static void writeOutput(String output, String outputFile) {
File file = new File(outputFile);
logger.debug("Output file: {}", file.getAbsolutePath());
try {
FileUtils.writeStringToFile(file, output, StandardCharsets.UTF_8);
} catch (IOException e) {
logger.error("Impossible to write output to file {}", outputFile, e);
System.exit(2);
}
}

public static void printHelp(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("openapi-diff <old> <new>", options);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.openapitools.openapidiff.core.exception;

public class RendererException extends RuntimeException {

public RendererException(Throwable cause) {
super(cause);
}

public RendererException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.openapidiff.core.exception.RendererException;
import org.openapitools.openapidiff.core.model.*;
import org.openapitools.openapidiff.core.utils.RefPointer;
import org.openapitools.openapidiff.core.utils.RefType;
Expand All @@ -21,81 +24,78 @@ public class ConsoleRender implements Render {
protected ChangedOpenApi diff;

@Override
public String render(ChangedOpenApi diff) {
public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
this.diff = diff;
StringBuilder output = new StringBuilder();
if (diff.isUnchanged()) {
output.append("No differences. Specifications are equivalents");
safelyAppend(outputStreamWriter, "No differences. Specifications are equivalents");
} else {
output
.append(bigTitle("Api Change Log"))
.append(StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH))
.append(System.lineSeparator());
safelyAppend(outputStreamWriter, bigTitle("Api Change Log"));
safelyAppend(
outputStreamWriter,
StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH));
safelyAppend(outputStreamWriter, System.lineSeparator());

List<Endpoint> newEndpoints = diff.getNewEndpoints();
String ol_newEndpoint = listEndpoints(newEndpoints, "What's New");
listEndpoints(newEndpoints, "What's New", outputStreamWriter);

List<Endpoint> missingEndpoints = diff.getMissingEndpoints();
String ol_missingEndpoint = listEndpoints(missingEndpoints, "What's Deleted");
listEndpoints(missingEndpoints, "What's Deleted", outputStreamWriter);

List<Endpoint> deprecatedEndpoints = diff.getDeprecatedEndpoints();
String ol_deprecatedEndpoint = listEndpoints(deprecatedEndpoints, "What's Deprecated");
listEndpoints(deprecatedEndpoints, "What's Deprecated", outputStreamWriter);

List<ChangedOperation> changedOperations = diff.getChangedOperations();
String ol_changed = ol_changed(changedOperations);

output
.append(renderBody(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed))
.append(title("Result"))
.append(
StringUtils.center(
diff.isCompatible()
? "API changes are backward compatible"
: "API changes broke backward compatibility",
LINE_LENGTH))
.append(System.lineSeparator())
.append(separator('-'));
ol_changed(changedOperations, outputStreamWriter);

safelyAppend(
outputStreamWriter,
StringUtils.center(
diff.isCompatible()
? "API changes are backward compatible"
: "API changes broke backward compatibility",
LINE_LENGTH));
safelyAppend(outputStreamWriter, System.lineSeparator());
safelyAppend(outputStreamWriter, separator('-'));
}
try {
outputStreamWriter.close();
} catch (IOException e) {
throw new RendererException(e);
}
return output.toString();
}

private String ol_changed(List<ChangedOperation> operations) {
private void ol_changed(
List<ChangedOperation> operations, OutputStreamWriter outputStreamWriter) {
if (null == operations || operations.isEmpty()) {
return "";
return;
}
StringBuilder sb = new StringBuilder();
sb.append(title("What's Changed"));
safelyAppend(outputStreamWriter, title("What's Changed"));
for (ChangedOperation operation : operations) {
String pathUrl = operation.getPathUrl();
String method = operation.getHttpMethod().toString();
String desc =
Optional.ofNullable(operation.getSummary()).map(ChangedMetadata::getRight).orElse("");

StringBuilder ul_detail = new StringBuilder();
if (result(operation.getParameters()).isDifferent()) {
ul_detail
.append(StringUtils.repeat(' ', 2))
.append("Parameter:")
.append(System.lineSeparator())
.append(ul_param(operation.getParameters()));
safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
safelyAppend(outputStreamWriter, "Parameter:");
safelyAppend(outputStreamWriter, System.lineSeparator());
safelyAppend(outputStreamWriter, ul_param(operation.getParameters()));
}
if (operation.resultRequestBody().isDifferent()) {
ul_detail
.append(StringUtils.repeat(' ', 2))
.append("Request:")
.append(System.lineSeparator())
.append(ul_content(operation.getRequestBody().getContent(), true));
safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
safelyAppend(outputStreamWriter, "Request:");
safelyAppend(outputStreamWriter, System.lineSeparator());
safelyAppend(outputStreamWriter, ul_content(operation.getRequestBody().getContent(), true));
}
if (operation.resultApiResponses().isDifferent()) {
ul_detail
.append(StringUtils.repeat(' ', 2))
.append("Return Type:")
.append(System.lineSeparator())
.append(ul_response(operation.getApiResponses()));
safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
safelyAppend(outputStreamWriter, "Return Type:");
safelyAppend(outputStreamWriter, System.lineSeparator());
safelyAppend(outputStreamWriter, ul_response(operation.getApiResponses()));
}
sb.append(itemEndpoint(method, pathUrl, desc)).append(ul_detail);
safelyAppend(outputStreamWriter, itemEndpoint(method, pathUrl, desc));
}
return sb.toString();
}

private String ul_response(ChangedApiResponse changedApiResponse) {
Expand Down Expand Up @@ -279,7 +279,8 @@ private String li_changedParam(ChangedParameter changeParam) {
}
}

private String listEndpoints(List<Endpoint> endpoints, String title) {
private String listEndpoints(
List<Endpoint> endpoints, String title, OutputStreamWriter outputStreamWriter) {
if (null == endpoints || endpoints.isEmpty()) {
return "";
}
Expand Down Expand Up @@ -317,8 +318,7 @@ public String title(String title, char ch) {
separator(ch), little, StringUtils.center(title, LINE_LENGTH - 4), little, separator(ch));
}

public StringBuilder separator(char ch) {
StringBuilder sb = new StringBuilder();
return sb.append(StringUtils.repeat(ch, LINE_LENGTH)).append(System.lineSeparator());
public String separator(char ch) {
return StringUtils.repeat(ch, LINE_LENGTH) + System.lineSeparator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import j2html.rendering.FlatHtml;
import j2html.tags.ContainerTag;
import j2html.tags.specialized.DivTag;
import j2html.tags.specialized.HtmlTag;
import j2html.tags.specialized.LiTag;
import j2html.tags.specialized.OlTag;
import j2html.tags.specialized.UlTag;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.openapitools.openapidiff.core.exception.RendererException;
import org.openapitools.openapidiff.core.model.ChangedApiResponse;
import org.openapitools.openapidiff.core.model.ChangedContent;
import org.openapitools.openapidiff.core.model.ChangedMediaType;
Expand Down Expand Up @@ -71,7 +75,7 @@ public HtmlRender(String title, String linkCss) {
this.linkCss = linkCss;
}

public String render(ChangedOpenApi diff) {
public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
this.diff = diff;

List<Endpoint> newEndpoints = diff.getNewEndpoints();
Expand All @@ -86,10 +90,16 @@ public String render(ChangedOpenApi diff) {
List<ChangedOperation> changedOperations = diff.getChangedOperations();
OlTag ol_changed = ol_changed(changedOperations);

return renderHtml(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed);
renderHtml(
ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed, outputStreamWriter);
}

public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_changed) {
public void renderHtml(
OlTag ol_new,
OlTag ol_miss,
OlTag ol_deprec,
OlTag ol_changed,
OutputStreamWriter outputStreamWriter) {
HtmlTag html =
html()
.attr("lang", "en")
Expand All @@ -110,7 +120,14 @@ public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_
div().with(h2("What's Deprecated"), hr(), ol_deprec),
div().with(h2("What's Changed"), hr(), ol_changed))));

return document().render() + html.render();
try {
FlatHtml<OutputStreamWriter> flatHtml = FlatHtml.into(outputStreamWriter);
document().render(flatHtml);
html.render(flatHtml);
outputStreamWriter.close();
} catch (IOException e) {
throw new RendererException("Problem rendering html document.", e);
}
}

private OlTag ol_newEndpoint(List<Endpoint> endpoints) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.file.Paths;
import java.io.OutputStreamWriter;
import org.openapitools.openapidiff.core.exception.RendererException;
import org.openapitools.openapidiff.core.model.ChangedOpenApi;

public class JsonRender implements Render {
Expand All @@ -17,21 +18,14 @@ public JsonRender() {
}

@Override
public String render(ChangedOpenApi diff) {
public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
try {
return objectMapper.writeValueAsString(diff);
objectMapper.writeValue(outputStreamWriter, diff);
outputStreamWriter.close();
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not serialize diff as JSON", e);
}
}

public void renderToFile(ChangedOpenApi diff, String file) {
try {
objectMapper.writeValue(Paths.get(file).toFile(), diff);
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not serialize diff as JSON", e);
throw new RendererException("Could not serialize diff as JSON", e);
} catch (IOException e) {
throw new RuntimeException("Could not write to JSON file", e);
throw new RendererException(e);
}
}
}
Loading