Skip to content

Report causes for compatibility breakage #105

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

Merged
merged 9 commits into from
Jan 8, 2020
71 changes: 71 additions & 0 deletions src/main/java/com/qdesrame/openapi/diff/output/ConsoleRender.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import static com.qdesrame.openapi.diff.model.Changed.result;

import com.qdesrame.openapi.diff.model.*;
import com.qdesrame.openapi.diff.utils.RefPointer;
import com.qdesrame.openapi.diff.utils.RefType;
import io.swagger.v3.oas.models.media.ArraySchema;
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.util.List;
Expand All @@ -13,11 +17,14 @@

public class ConsoleRender implements Render {
private static final int LINE_LENGTH = 74;
protected static RefPointer<Schema> refPointer = new RefPointer<>(RefType.SCHEMAS);
protected ChangedOpenApi diff;

private StringBuilder output;

@Override
public String render(ChangedOpenApi diff) {
this.diff = diff;
output = new StringBuilder();
if (diff.isUnchanged()) {
output.append("No differences. Specifications are equivalents");
Expand Down Expand Up @@ -168,9 +175,73 @@ private String itemContent(
.append("Schema: ")
.append(changedMediaType.isCompatible() ? "Backward compatible" : "Broken compatibility")
.append(System.lineSeparator());
if (!changedMediaType.isCompatible()) {
sb.append(incompatibilities(changedMediaType.getSchema()));
}
return sb.toString();
}

private String incompatibilities(final ChangedSchema schema) {
return incompatibilities("", schema);
}

private String incompatibilities(String propName, final ChangedSchema schema) {
StringBuilder sb = new StringBuilder();
if (schema.getItems() != null) {
sb.append(items(propName, schema.getItems()));
}
if (schema.isCoreChanged() == DiffResult.INCOMPATIBLE && schema.isChangedType()) {
String type = type(schema.getOldSchema()) + " -> " + type(schema.getNewSchema());
sb.append(property(propName, "Changed property type", type));
}
String prefix = propName.isEmpty() ? "" : propName + ".";
sb.append(
properties(prefix, "Missing property", schema.getMissingProperties(), schema.getContext()));
schema
.getChangedProperties()
.forEach((name, property) -> sb.append(incompatibilities(prefix + name, property)));
return sb.toString();
}

private String items(String propName, ChangedSchema schema) {
StringBuilder sb = new StringBuilder();
sb.append(incompatibilities(propName + "[n]", schema));
return sb.toString();
}

private String properties(
String propPrefix, String title, Map<String, Schema> properties, DiffContext context) {
StringBuilder sb = new StringBuilder();
if (properties != null) {
properties.forEach(
(key, value) -> sb.append(property(propPrefix + key, title, resolve(value))));
}
return sb.toString();
}

protected String property(String name, String title, Schema schema) {
return property(name, title, type(schema));
}

protected String property(String name, String title, String type) {
return String.format("%s%s: %s (%s)\n", StringUtils.repeat(' ', 10), title, name, type);
}

protected Schema resolve(Schema schema) {
return refPointer.resolveRef(
diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref());
}

protected String type(Schema schema) {
String result = "object";
if (schema instanceof ArraySchema) {
result = "array";
} else if (schema.getType() != null) {
result = schema.getType();
}
return result;
}

private String ul_param(ChangedParameters changedParameters) {
List<Parameter> addParameters = changedParameters.getIncreased();
List<Parameter> delParameters = changedParameters.getMissing();
Expand Down
78 changes: 75 additions & 3 deletions src/main/java/com/qdesrame/openapi/diff/output/HtmlRender.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import static j2html.TagCreator.*;

import com.qdesrame.openapi.diff.model.*;
import com.qdesrame.openapi.diff.utils.RefPointer;
import com.qdesrame.openapi.diff.utils.RefType;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.MediaType;
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.tags.ContainerTag;
Expand All @@ -16,6 +20,8 @@ public class HtmlRender implements Render {

private String title;
private String linkCss;
protected static RefPointer<Schema> refPointer = new RefPointer<>(RefType.SCHEMAS);
protected ChangedOpenApi diff;

public HtmlRender() {
this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
Expand All @@ -27,6 +33,8 @@ public HtmlRender(String title, String linkCss) {
}

public String render(ChangedOpenApi diff) {
this.diff = diff;

List<Endpoint> newEndpoints = diff.getNewEndpoints();
ContainerTag ol_newEndpoint = ol_newEndpoint(newEndpoints);

Expand Down Expand Up @@ -214,16 +222,80 @@ private ContainerTag li_missingRequest(String name, MediaType request) {
}

private ContainerTag li_changedRequest(String name, ChangedMediaType request) {
return li().withText(String.format("Changed body: '%s'", name))
.with(div_changedSchema(request.getSchema()));
ContainerTag li =
li().with(div_changedSchema(request.getSchema()))
.withText(String.format("Changed body: '%s'", name));
if (request.isIncompatible()) {
incompatibilities(li, request.getSchema());
}
return li;
}

private ContainerTag div_changedSchema(ChangedSchema schema) {
ContainerTag div = div();
div.with(h3("Schema"));
div.with(h3("Schema" + (schema.isIncompatible() ? " incompatible" : "")));
return div;
}

private void incompatibilities(final ContainerTag output, final ChangedSchema schema) {
incompatibilities(output, "", schema);
}

private void incompatibilities(
final ContainerTag output, String propName, final ChangedSchema schema) {
if (schema.getItems() != null) {
items(output, propName, schema.getItems());
}
if (schema.isCoreChanged() == DiffResult.INCOMPATIBLE && schema.isChangedType()) {
String type = type(schema.getOldSchema()) + " -> " + type(schema.getNewSchema());
property(output, propName, "Changed property type", type);
}
String prefix = propName.isEmpty() ? "" : propName + ".";
properties(
output, prefix, "Missing property", schema.getMissingProperties(), schema.getContext());
schema
.getChangedProperties()
.forEach((name, property) -> incompatibilities(output, prefix + name, property));
}

private void items(ContainerTag output, String propName, ChangedSchema schema) {
incompatibilities(output, propName + "[n]", schema);
}

private void properties(
ContainerTag output,
String propPrefix,
String title,
Map<String, Schema> properties,
DiffContext context) {
if (properties != null) {
properties.forEach((key, value) -> property(output, propPrefix + key, title, resolve(value)));
}
}

protected void property(ContainerTag output, String name, String title, Schema schema) {
property(output, name, title, type(schema));
}

protected void property(ContainerTag output, String name, String title, String type) {
output.with(p(String.format("%s: %s (%s)", title, name, type)).withClass("missing"));
}

protected Schema resolve(Schema schema) {
return refPointer.resolveRef(
diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref());
}

protected String type(Schema schema) {
String result = "object";
if (schema instanceof ArraySchema) {
result = "array";
} else if (schema.getType() != null) {
result = schema.getType();
}
return result;
}

private ContainerTag ul_param(ChangedParameters changedParameters) {
List<Parameter> addParameters = changedParameters.getIncreased();
List<Parameter> delParameters = changedParameters.getMissing();
Expand Down