From 1f19abbb445737096e7561eab839d34c4e60e963 Mon Sep 17 00:00:00 2001 From: Julien LAMY Date: Wed, 7 Feb 2018 17:12:01 +0100 Subject: [PATCH 01/18] Upgrade Java & add lombok --- pom.xml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 320aae82..253356f4 100644 --- a/pom.xml +++ b/pom.xml @@ -58,12 +58,12 @@ io.swagger swagger-parser - 1.0.31 + 1.0.34 io.swagger swagger-compat-spec-parser - 1.0.31 + 1.0.34 com.j2html @@ -81,6 +81,11 @@ 4.8.2 test + + org.projectlombok + lombok + 1.16.20 + @@ -91,8 +96,8 @@ maven-compiler-plugin - 1.6 - 1.6 + 1.8 + 1.8 UTF-8 3.2 @@ -165,8 +170,8 @@ maven-compiler-plugin - 1.6 - 1.6 + 1.8 + 1.8 UTF-8 3.2 From abc9605dd0b5ba881fd5886f2cc0c82746eae88d Mon Sep 17 00:00:00 2001 From: Julien LAMY Date: Wed, 7 Feb 2018 17:16:29 +0100 Subject: [PATCH 02/18] Add check backwards incompatibilities --- .gitignore | 6 + .../deepoove/swagger/diff/SwaggerDiff.java | 59 +- .../swagger/diff/compare/ModelDiff.java | 198 +-- .../swagger/diff/compare/ParameterDiff.java | 231 ++-- .../swagger/diff/compare/PropertyDiff.java | 83 +- .../deepoove/swagger/diff/model/Changed.java | 7 +- .../swagger/diff/model/ChangedEndpoint.java | 88 +- .../swagger/diff/model/ChangedOperation.java | 115 +- .../swagger/diff/model/ChangedParameter.java | 103 +- .../swagger/diff/model/ElProperty.java | 24 +- .../deepoove/swagger/diff/model/Endpoint.java | 52 +- .../swagger/diff/output/HtmlRender.java | 483 +++++--- .../swagger/diff/output/MarkdownRender.java | 397 +++--- src/main/resources/demo.css | 5 + src/main/resources/template.html | 31 - ...gerDiffBackwardsIncompatibilitiesTest.java | 147 +++ .../petstore_v2_withAddRequiredParam.json | 1001 ++++++++++++++++ ...tstore_v2_withAddRequiredPropertyBody.json | 999 ++++++++++++++++ ..._v2_withAllBackwardsIncompatibilities.json | 949 +++++++++++++++ ...petstore_v2_withChangeParamToRequired.json | 994 +++++++++++++++ ...e_v2_withChangePropertyBodyToRequired.json | 996 +++++++++++++++ .../petstore_v2_withChangeTypePathParam.json | 993 +++++++++++++++ ...ore_v2_withChangeTypePropertyResponse.json | 994 +++++++++++++++ .../petstore_v2_withChangeTypeQueryParam.json | 984 +++++++++++++++ .../petstore_v2_withChangesCompatibles.json | 1064 +++++++++++++++++ .../petstore_v2_withDeleteParam.json | 977 +++++++++++++++ ...etstore_v2_withDeletePropertyResponse.json | 991 +++++++++++++++ .../petstore_v2_withTypePropertyBody.json | 994 +++++++++++++++ 28 files changed, 13038 insertions(+), 927 deletions(-) delete mode 100644 src/main/resources/template.html create mode 100644 src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredParam.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangeParamToRequired.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePathParam.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePropertyResponse.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypeQueryParam.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withDeleteParam.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withDeletePropertyResponse.json create mode 100644 src/test/resources/backwards_incompatibilities/petstore_v2_withTypePropertyBody.json diff --git a/.gitignore b/.gitignore index 82f1c837..437d08f8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* /target/ + +\.DS_Store + +*.html + +testDiff\.md diff --git a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java index f4d67e05..ff7c1086 100644 --- a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java @@ -28,7 +28,9 @@ import io.swagger.models.properties.Property; import io.swagger.parser.SwaggerCompatConverter; import io.swagger.parser.SwaggerParser; +import lombok.Data; +@Data public class SwaggerDiff { public static final String SWAGGER_VERSION_V2 = "2.0"; @@ -44,30 +46,30 @@ public class SwaggerDiff { /** * compare two swagger 1.x doc - * + * * @param oldSpec * old api-doc location:Json or Http * @param newSpec * new api-doc location:Json or Http */ - public static SwaggerDiff compareV1(String oldSpec, String newSpec) { + public static SwaggerDiff compareV1(final String oldSpec, final String newSpec) { return compare(oldSpec, newSpec, null, null); } /** * compare two swagger v2.0 doc - * + * * @param oldSpec * old api-doc location:Json or Http * @param newSpec * new api-doc location:Json or Http */ - public static SwaggerDiff compareV2(String oldSpec, String newSpec) { + public static SwaggerDiff compareV2(final String oldSpec, final String newSpec) { return compare(oldSpec, newSpec, null, SWAGGER_VERSION_V2); } - public static SwaggerDiff compare(String oldSpec, String newSpec, - List auths, String version) { + public static SwaggerDiff compare(final String oldSpec, final String newSpec, + final List auths, final String version) { return new SwaggerDiff(oldSpec, newSpec, auths, version).compare(); } @@ -77,8 +79,8 @@ public static SwaggerDiff compare(String oldSpec, String newSpec, * @param auths * @param version */ - private SwaggerDiff(String oldSpec, String newSpec, List auths, - String version) { + private SwaggerDiff(final String oldSpec, final String newSpec, final List auths, + final String version) { if (SWAGGER_VERSION_V2.equals(version)) { SwaggerParser swaggerParser = new SwaggerParser(); oldSpecSwagger = swaggerParser.read(oldSpec, auths, true); @@ -140,7 +142,7 @@ private SwaggerDiff compare() { .diff(oldParameters, newParameters); changedOperation.setAddParameters(parameterDiff.getIncreased()); changedOperation.setMissingParameters(parameterDiff.getMissing()); - changedOperation.setChangedParameter(parameterDiff.getChanged()); + changedOperation.setChangedParameters(parameterDiff.getChanged()); Property oldResponseProperty = getResponseProperty(oldOperation); Property newResponseProperty = getResponseProperty(newOperation); @@ -149,6 +151,7 @@ private SwaggerDiff compare() { propertyDiff.diff(oldResponseProperty, newResponseProperty); changedOperation.setAddProps(propertyDiff.getIncreased()); changedOperation.setMissingProps(propertyDiff.getMissing()); + changedOperation.setChangedProps(propertyDiff.getTypeChanges()); if (changedOperation.isDiff()) { operas.put(method, changedOperation); @@ -169,15 +172,17 @@ private SwaggerDiff compare() { return this; } - private Property getResponseProperty(Operation operation) { + private Property getResponseProperty(final Operation operation) { Map responses = operation.getResponses(); Response response = responses.get("200"); return null == response ? null : response.getSchema(); } - private List convert2EndpointList(Map map) { + private List convert2EndpointList(final Map map) { List endpoints = new ArrayList(); - if (null == map) return endpoints; + if (null == map) { + return endpoints; + } for (Entry entry : map.entrySet()) { String url = entry.getKey(); Path path = entry.getValue(); @@ -199,10 +204,12 @@ private List convert2EndpointList(Map map) { return endpoints; } - private Collection convert2EndpointList(String pathUrl, - Map map) { + private Collection convert2EndpointList(final String pathUrl, + final Map map) { List endpoints = new ArrayList(); - if (null == map) return endpoints; + if (null == map) { + return endpoints; + } for (Entry entry : map.entrySet()) { HttpMethod httpMethod = entry.getKey(); Operation operation = entry.getValue(); @@ -216,16 +223,18 @@ private Collection convert2EndpointList(String pathUrl, return endpoints; } - public List getNewEndpoints() { - return newEndpoints; - } - - public List getMissingEndpoints() { - return missingEndpoints; - } - - public List getChangedEndpoints() { - return changedEndpoints; + public boolean isBackwardsCompatible() { + // Si la comparaison contient une modification flaguée comme non rétro-compatible, on renvoie false + if(!getMissingEndpoints().isEmpty()) { + return false; + } else { + for (ChangedEndpoint changedEndpoint : getChangedEndpoints()) { + if(!changedEndpoint.isBackwardsCompatible()) { + return false; + } + } + } + return true; } } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java index e97d32f0..4673d2d7 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -11,108 +12,115 @@ import io.swagger.models.Model; import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; +import lombok.Data; /** * compare two model * @author Sayi - * @version + * @version */ +@Data public class ModelDiff { - private List increased; - private List missing; + private List increased; + private List missing; + private List requiredChanges = new ArrayList(); + private List typeChanges = new ArrayList(); + + Map oldDedinitions; + Map newDedinitions; + + private ModelDiff() { + increased = new ArrayList(); + missing = new ArrayList(); + requiredChanges = new ArrayList(); + typeChanges = new ArrayList(); + } + + public static ModelDiff buildWithDefinition(final Map left, + final Map right) { + ModelDiff diff = new ModelDiff(); + diff.oldDedinitions = left; + diff.newDedinitions = right; + return diff; + } + + public ModelDiff diff(final Model leftModel, final Model rightModel) { + return this.diff(leftModel, rightModel, null); + } + + public ModelDiff diff(final Model leftModel, final Model rightModel, final String parentEl) { + if (null == leftModel && null == rightModel) { + return this; + } + Map leftProperties = null == leftModel ? null : leftModel.getProperties(); + Map rightProperties = null == rightModel ? null : rightModel.getProperties(); + MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); + Map increasedProp = propertyDiff.getIncreased(); + Map missingProp = propertyDiff.getMissing(); + + increased.addAll(convert2ElPropertys(increasedProp, parentEl, false)); + missing.addAll(convert2ElPropertys(missingProp, parentEl, true)); + + List sharedKey = propertyDiff.getSharedKey(); + for (String key : sharedKey) { + Property left = leftProperties.get(key); + Property right = rightProperties.get(key); + // Check if type change + if(left.getType() != null && right.getType() != null && !left.getType().equals(right.getType())) { + Map map = new HashMap(); // TODO JLA ? + map.put(key, right); + typeChanges.addAll(convert2ElPropertys(map, parentEl , true)); + } + // Check if property becomes required + if(left.getRequired() != right.getRequired() && right.getRequired()) { + Map map = new HashMap(); // TODO JLA ? + map.put(key, right); + requiredChanges.addAll(convert2ElPropertys(map, parentEl , true)); + } + if (left instanceof RefProperty + && right instanceof RefProperty) { + String leftRef = ((RefProperty) left).getSimpleRef(); + String rightRef = ((RefProperty) right).getSimpleRef(); + diff(oldDedinitions.get(leftRef), + newDedinitions.get(rightRef), + null == parentEl ? key : (parentEl + "." + key)); + } + } + return this; + } + + private Collection convert2ElPropertys( + final Map propMap, final String parentEl, final boolean isLeft) { + List result = new ArrayList(); + if (null == propMap) { + return result; + } + for (Entry entry : propMap.entrySet()) { + String propName = entry.getKey(); + Property property = entry.getValue(); + if (property instanceof RefProperty) { + String ref = ((RefProperty) property).getSimpleRef(); + Model model = isLeft ? oldDedinitions.get(ref) + : newDedinitions.get(ref); + if (model != null) { + Map properties = model.getProperties(); + result.addAll( + convert2ElPropertys(properties, + null == parentEl ? propName + : (parentEl + "." + propName), + isLeft)); + } + } else { + ElProperty pWithPath = new ElProperty(); + pWithPath.setProperty(property); + pWithPath.setEl(null == parentEl ? propName + : (parentEl + "." + propName)); + result.add(pWithPath); + } + } + return result; + } - Map oldDedinitions; - Map newDedinitions; - - private ModelDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - } - - public static ModelDiff buildWithDefinition(Map left, - Map right) { - ModelDiff diff = new ModelDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; - return diff; - } - - public ModelDiff diff(Model leftModel, Model rightModel) { - return this.diff(leftModel, rightModel, null); - } - - public ModelDiff diff(Model leftModel, Model rightModel, String parentEl) { - if (null == leftModel && null == rightModel) return this; - Map leftProperties = null == leftModel ? null : leftModel.getProperties(); - Map rightProperties = null == rightModel ? null : rightModel.getProperties(); - MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); - Map increasedProp = propertyDiff.getIncreased(); - Map missingProp = propertyDiff.getMissing(); - - increased.addAll(convert2ElPropertys(increasedProp, parentEl, false)); - missing.addAll(convert2ElPropertys(missingProp, parentEl, true)); - - List sharedKey = propertyDiff.getSharedKey(); - for (String key : sharedKey) { - Property left = leftProperties.get(key); - Property right = rightProperties.get(key); - if (left instanceof RefProperty - && right instanceof RefProperty) { - String leftRef = ((RefProperty) left).getSimpleRef(); - String rightRef = ((RefProperty) right).getSimpleRef(); - diff(oldDedinitions.get(leftRef), - newDedinitions.get(rightRef), - null == parentEl ? key : (parentEl + "." + key)); - } - } - return this; - } - - private Collection convert2ElPropertys( - Map propMap, String parentEl, boolean isLeft) { - List result = new ArrayList(); - if (null == propMap) return result; - for (Entry entry : propMap.entrySet()) { - String propName = entry.getKey(); - Property property = entry.getValue(); - if (property instanceof RefProperty) { - String ref = ((RefProperty) property).getSimpleRef(); - Model model = isLeft ? oldDedinitions.get(ref) - : newDedinitions.get(ref); - if (model != null) { - Map properties = model.getProperties(); - result.addAll( - convert2ElPropertys(properties, - null == parentEl ? propName - : (parentEl + "." + propName), - isLeft)); - } - } else { - ElProperty pWithPath = new ElProperty(); - pWithPath.setProperty(property); - pWithPath.setEl(null == parentEl ? propName - : (parentEl + "." + propName)); - result.add(pWithPath); - } - } - return result; - } - - public List getIncreased() { - return increased; - } - - public void setIncreased(List increased) { - this.increased = increased; - } - - public List getMissing() { - return missing; - } - - public void setMissing(List missing) { - this.missing = missing; - } } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java index c0c887a5..aec496fa 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java @@ -12,125 +12,130 @@ import io.swagger.models.RefModel; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.Parameter; +import lombok.Data; /** * compare two parameter * @author Sayi - * @version + * @version */ +@Data public class ParameterDiff { - private List increased; - private List missing; - private List changed; - - Map oldDedinitions; - Map newDedinitions; - - private ParameterDiff(){} - - public static ParameterDiff buildWithDefinition(Map left, - Map right) { - ParameterDiff diff = new ParameterDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; - return diff; - } - - public ParameterDiff diff(List left, - List right) { - ParameterDiff instance = new ParameterDiff(); - if (null == left) left = new ArrayList(); - if (null == right) right = new ArrayList(); - - instance.increased = new ArrayList(right); - instance.missing = new ArrayList(); - instance.changed = new ArrayList(); - for (Parameter leftPara : left){ - String name = leftPara.getName(); - int index = index(instance.increased, name); - if (-1 == index){ - instance.missing.add(leftPara); - }else{ - Parameter rightPara = instance.increased.get(index); - instance.increased.remove(index); - - ChangedParameter changedParameter = new ChangedParameter(); - changedParameter.setLeftParameter(leftPara); - changedParameter.setRightParameter(rightPara); - - if (leftPara instanceof BodyParameter && rightPara instanceof BodyParameter){ - BodyParameter leftBodyPara = (BodyParameter)leftPara; - Model leftSchema = leftBodyPara.getSchema(); - BodyParameter rightBodyPara = (BodyParameter)rightPara; - Model rightSchema = rightBodyPara.getSchema(); - if (leftSchema instanceof RefModel && rightSchema instanceof RefModel){ - String leftRef = ((RefModel) leftSchema).getSimpleRef(); - String rightRef = ((RefModel) rightSchema).getSimpleRef(); - Model leftModel = oldDedinitions.get(leftRef); - Model rightModel = newDedinitions.get(rightRef); - ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions).diff(leftModel, rightModel, name); - changedParameter.setIncreased(diff.getIncreased()); - changedParameter.setMissing(diff.getMissing()); - } - } - - - //is requried - boolean rightRequired = rightPara.getRequired(); - boolean leftRequired = leftPara.getRequired(); - changedParameter.setChangeRequired(leftRequired != rightRequired); - - //description - String description = rightPara.getDescription(); - String oldPescription = leftPara.getDescription(); - if (StringUtils.isBlank(description)) description = ""; - if (StringUtils.isBlank(oldPescription)) oldPescription = ""; - changedParameter.setChangeDescription(!description.equals(oldPescription)); - - if (changedParameter.isDiff()){ - instance.changed.add(changedParameter); - } - - } - - } - return instance; - } - - private static int index(List right, String name) { - int i = 0; - for (; i < right.size(); i++){ - Parameter para = right.get(i); - if (name.equals(para.getName())){ - return i; - } - } - return -1; - } - - public List getIncreased() { - return increased; - } - - public void setIncreased(List increased) { - this.increased = increased; - } - - public List getMissing() { - return missing; - } - - public void setMissing(List missing) { - this.missing = missing; - } - - public List getChanged() { - return changed; - } - - public void setChanged(List changed) { - this.changed = changed; - } + private List increased; + private List missing; + private List changed; + + Map oldDedinitions; + Map newDedinitions; + + private ParameterDiff(){} + + public static ParameterDiff buildWithDefinition(final Map left, + final Map right) { + ParameterDiff diff = new ParameterDiff(); + diff.oldDedinitions = left; + diff.newDedinitions = right; + return diff; + } + + public ParameterDiff diff(List left, + List right) { + ParameterDiff instance = new ParameterDiff(); + if (null == left) { + left = new ArrayList(); + } + if (null == right) { + right = new ArrayList(); + } + + instance.increased = new ArrayList(right); + instance.missing = new ArrayList(); + instance.changed = new ArrayList(); + for (Parameter leftPara : left){ + String name = leftPara.getName(); + int index = index(instance.increased, name); + if (-1 == index){ + instance.missing.add(leftPara); + }else{ + Parameter rightPara = instance.increased.get(index); + instance.increased.remove(index); + ChangedParameter changedParameter = new ChangedParameter(); + changedParameter.setLeftParameter(leftPara); + changedParameter.setRightParameter(rightPara); + + if (leftPara instanceof BodyParameter && rightPara instanceof BodyParameter){ + BodyParameter leftBodyPara = (BodyParameter)leftPara; + Model leftSchema = leftBodyPara.getSchema(); + BodyParameter rightBodyPara = (BodyParameter)rightPara; + Model rightSchema = rightBodyPara.getSchema(); + if (leftSchema instanceof RefModel && rightSchema instanceof RefModel){ + String leftRef = ((RefModel) leftSchema).getSimpleRef(); + String rightRef = ((RefModel) rightSchema).getSimpleRef(); + Model leftModel = oldDedinitions.get(leftRef); + Model rightModel = newDedinitions.get(rightRef); + ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions).diff(leftModel, rightModel, name); + changedParameter.setIncreased(diff.getIncreased()); + changedParameter.setMissing(diff.getMissing()); + changedParameter.setRequiredChanges(diff.getRequiredChanges()); + changedParameter.setTypesChanges(diff.getTypeChanges()); + } + } + + + //is required + boolean rightRequired = rightPara.getRequired(); + boolean leftRequired = leftPara.getRequired(); + changedParameter.setChangeRequired(leftRequired != rightRequired && rightRequired); + + // TODO JLA +// //is change type +// if(rightPara.getName().contains("username")){ +// SerializableParameter s = (SerializableParameter) rightPara; +// System.out.println(rightPara.getDescription()); +// System.out.println(s.getName()); +// System.out.println(s.getIn()); +// System.out.println(s.getType()); +// } +// if(leftPara.getName().contains("username")){ +// SerializableParameter s = (SerializableParameter) leftPara; +// System.out.println(leftPara.getDescription()); +// System.out.println(s.getName()); +// System.out.println(s.getIn()); +// System.out.println(s.getType()); +// } + + //description + String description = rightPara.getDescription(); + String oldPescription = leftPara.getDescription(); + if (StringUtils.isBlank(description)) { + description = ""; + } + if (StringUtils.isBlank(oldPescription)) { + oldPescription = ""; + } + changedParameter.setChangeDescription(!description.equals(oldPescription)); + + if (changedParameter.isDiff()){ + instance.changed.add(changedParameter); + } + + } + + } + return instance; + } + + private static int index(final List right, final String name) { + int i = 0; + for (; i < right.size(); i++){ + Parameter para = right.get(i); + if (name.equals(para.getName())){ + return i; + } + } + return -1; + } + } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java index 0abf9372..7b5224a0 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java @@ -9,55 +9,44 @@ import io.swagger.models.Model; import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; +import lombok.Data; +@Data public class PropertyDiff { - private List increased; - private List missing; - - Map oldDedinitions; - Map newDedinitions; - - private PropertyDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - } - - public static PropertyDiff buildWithDefinition(Map left, - Map right) { - PropertyDiff diff = new PropertyDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; - return diff; - } - - public PropertyDiff diff(Property left, Property right) { - if ((null == left || left instanceof RefProperty) && (null == right || right instanceof RefProperty)) { - Model leftModel = null == left ? null : oldDedinitions.get(((RefProperty) left).getSimpleRef()); - Model rightModel = null == right ? null : newDedinitions.get(((RefProperty) right).getSimpleRef()); - ModelDiff diff = ModelDiff - .buildWithDefinition(oldDedinitions, newDedinitions) - .diff(leftModel, rightModel); - increased.addAll(diff.getIncreased()); - missing.addAll(diff.getMissing()); - } - return this; - } - - public List getIncreased() { - return increased; - } - - public void setIncreased(List increased) { - this.increased = increased; - } - - public List getMissing() { - return missing; - } - - public void setMissing(List missing) { - this.missing = missing; - } + private List increased; + private List missing; + private List typeChanges; + + Map oldDedinitions; + Map newDedinitions; + + private PropertyDiff() { + increased = new ArrayList(); + missing = new ArrayList(); + typeChanges = new ArrayList(); + } + + public static PropertyDiff buildWithDefinition(final Map left, + final Map right) { + PropertyDiff diff = new PropertyDiff(); + diff.oldDedinitions = left; + diff.newDedinitions = right; + return diff; + } + + public PropertyDiff diff(final Property left, final Property right) { + if ((null == left || left instanceof RefProperty) && (null == right || right instanceof RefProperty)) { + Model leftModel = null == left ? null : oldDedinitions.get(((RefProperty) left).getSimpleRef()); + Model rightModel = null == right ? null : newDedinitions.get(((RefProperty) right).getSimpleRef()); + ModelDiff diff = ModelDiff + .buildWithDefinition(oldDedinitions, newDedinitions) + .diff(leftModel, rightModel); + increased.addAll(diff.getIncreased()); + missing.addAll(diff.getMissing()); + typeChanges.addAll(diff.getTypeChanges()); + } + return this; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/Changed.java b/src/main/java/com/deepoove/swagger/diff/model/Changed.java index 9dfc0df7..ee5f45dc 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/Changed.java +++ b/src/main/java/com/deepoove/swagger/diff/model/Changed.java @@ -2,6 +2,11 @@ public interface Changed { - boolean isDiff(); + boolean isDiff(); + + /** + * @return check if the changes are backward compatible. + */ + boolean isBackwardsCompatible(); } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java index bb2e5efc..1b65c8a0 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java @@ -7,53 +7,69 @@ public class ChangedEndpoint implements Changed{ - private String pathUrl; + private String pathUrl; - private Map newOperations; - private Map missingOperations; + private Map newOperations; + private Map missingOperations; - private Map changedOperations; + private Map changedOperations; - public Map getNewOperations() { - return newOperations; - } + public Map getNewOperations() { + return newOperations; + } - public void setNewOperations(Map newOperations) { - this.newOperations = newOperations; - } + public void setNewOperations(final Map newOperations) { + this.newOperations = newOperations; + } - public Map getMissingOperations() { - return missingOperations; - } + public Map getMissingOperations() { + return missingOperations; + } - public void setMissingOperations( - Map missingOperations) { - this.missingOperations = missingOperations; - } - + public void setMissingOperations( + final Map missingOperations) { + this.missingOperations = missingOperations; + } - public Map getChangedOperations() { - return changedOperations; - } - public void setChangedOperations( - Map changedOperations) { - this.changedOperations = changedOperations; - } + public Map getChangedOperations() { + return changedOperations; + } - public String getPathUrl() { - return pathUrl; - } + public void setChangedOperations( + final Map changedOperations) { + this.changedOperations = changedOperations; + } - public void setPathUrl(String pathUrl) { - this.pathUrl = pathUrl; - } + public String getPathUrl() { + return pathUrl; + } - public boolean isDiff() { -// newOperations.isEmpty() + public void setPathUrl(final String pathUrl) { + this.pathUrl = pathUrl; + } + + @Override + public boolean isDiff() { +// newOperations.isEmpty() // || !missingOperations.isEmpty() -// || - return !changedOperations.isEmpty(); - } +// || + return !changedOperations.isEmpty(); + } + + @Override + public boolean isBackwardsCompatible() { + if(!missingOperations.isEmpty()) { + return false; + } else { + for (ChangedOperation changedOperation : changedOperations.values()) { + if(!changedOperation.isBackwardsCompatible()) { + return false; + } + } + } + return true; + } + } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java index a075ca61..cd6c132e 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java @@ -4,79 +4,52 @@ import java.util.List; import io.swagger.models.parameters.Parameter; +import lombok.Data; +@Data public class ChangedOperation implements Changed { - private String summary; - - private List addParameters = new ArrayList(); - private List missingParameters = new ArrayList(); - - private List changedParameter = new ArrayList(); - - private List addProps = new ArrayList(); - private List missingProps = new ArrayList(); - - public List getAddParameters() { - return addParameters; - } - - public void setAddParameters(List addParameters) { - this.addParameters = addParameters; - } - - public List getMissingParameters() { - return missingParameters; - } - - public void setMissingParameters(List missingParameters) { - this.missingParameters = missingParameters; - } - - public List getChangedParameter() { - return changedParameter; - } - - public void setChangedParameter(List changedParameter) { - this.changedParameter = changedParameter; - } - - public List getAddProps() { - return addProps; - } - - public void setAddProps(List addProps) { - this.addProps = addProps; - } - - public List getMissingProps() { - return missingProps; - } - - public void setMissingProps(List missingProps) { - this.missingProps = missingProps; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public boolean isDiff() { - return !addParameters.isEmpty() || !missingParameters.isEmpty() - || !changedParameter.isEmpty() || !addProps.isEmpty() - || !missingProps.isEmpty(); - } - public boolean isDiffProp(){ - return !addProps.isEmpty() - || !missingProps.isEmpty(); - } - public boolean isDiffParam(){ - return !addParameters.isEmpty() || !missingParameters.isEmpty() - || !changedParameter.isEmpty(); - } + private String summary; + + private List addParameters = new ArrayList(); + private List missingParameters = new ArrayList(); + private List changedParameters = new ArrayList(); + + private List addProps = new ArrayList(); + private List missingProps = new ArrayList(); + private List changedProps = new ArrayList(); + + @Override + public boolean isDiff() { + return isDiffProp() || isDiffParam(); + } + public boolean isDiffProp(){ + return !addProps.isEmpty() + || !missingProps.isEmpty() + || !changedProps.isEmpty(); + } + public boolean isDiffParam(){ + return !addParameters.isEmpty() || !missingParameters.isEmpty() + || !changedParameters.isEmpty(); + } + + @Override + public boolean isBackwardsCompatible() { + if(!missingProps.isEmpty() || !missingParameters.isEmpty() || !changedProps.isEmpty()) { + return false; + } else { + for (ChangedParameter changedParameter : getChangedParameters()) { + if(!changedParameter.isBackwardsCompatible()) { + return false; + } + } + for (Parameter parameter : addParameters) { + if(parameter.getRequired()) { + return false; + } + } + } + return true; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java index 42cb95e7..337055c7 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java @@ -4,70 +4,45 @@ import java.util.List; import io.swagger.models.parameters.Parameter; +import lombok.Data; +@Data public class ChangedParameter implements Changed { - - private List increased = new ArrayList(); - private List missing = new ArrayList();; - private Parameter leftParameter; - private Parameter rightParameter; - - private boolean isChangeRequired; - // private boolean isChangeType; - private boolean isChangeDescription; - - public boolean isChangeRequired() { - return isChangeRequired; - } - - public void setChangeRequired(boolean isChangeRequired) { - this.isChangeRequired = isChangeRequired; - } - - public boolean isChangeDescription() { - return isChangeDescription; - } - - public void setChangeDescription(boolean isChangeDescription) { - this.isChangeDescription = isChangeDescription; - } - - public Parameter getLeftParameter() { - return leftParameter; - } - - public void setLeftParameter(Parameter leftParameter) { - this.leftParameter = leftParameter; - } - - public Parameter getRightParameter() { - return rightParameter; - } - - public void setRightParameter(Parameter rightParameter) { - this.rightParameter = rightParameter; - } - - public boolean isDiff() { - return isChangeRequired || isChangeDescription || !increased.isEmpty() || !missing.isEmpty(); - } - - public List getIncreased() { - return increased; - } - - public void setIncreased(List increased) { - this.increased = increased; - } - - public List getMissing() { - return missing; - } - - public void setMissing(List missing) { - this.missing = missing; - } - - -} + private List increased = new ArrayList(); + private List missing = new ArrayList(); + private List requiredChanges = new ArrayList(); + private List typesChanges = new ArrayList(); + + private Parameter leftParameter; + private Parameter rightParameter; + + private boolean isChangeRequired; +// private boolean isChangeType; // TODO JLA hard ? + private boolean isChangeDescription; + + @Override + public boolean isDiff() { + return isChangeRequired + || isChangeDescription + || !increased.isEmpty() + || !missing.isEmpty() + || !requiredChanges.isEmpty() + || !typesChanges.isEmpty(); + } + + @Override + public boolean isBackwardsCompatible() { + boolean isBackwardsCompatible = !isChangeRequired + && missing.isEmpty() + && requiredChanges.isEmpty() + && typesChanges.isEmpty(); + for (ElProperty elProperty : increased) { + if(elProperty.getProperty() != null && elProperty.getProperty().getRequired()) { + return false; + } + } + return isBackwardsCompatible; + } + +} \ No newline at end of file diff --git a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java index 807c3ceb..56ba3f3c 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java @@ -1,32 +1,18 @@ package com.deepoove.swagger.diff.model; import io.swagger.models.properties.Property; +import lombok.Data; /** * property with expression Language grammar * @author Sayi - * @version + * @version */ +@Data public class ElProperty { - private String el; + private String el; - private Property property; - - public Property getProperty() { - return property; - } - - public void setProperty(Property property) { - this.property = property; - } - - public String getEl() { - return el; - } - - public void setEl(String el) { - this.el = el; - } + private Property property; } diff --git a/src/main/java/com/deepoove/swagger/diff/model/Endpoint.java b/src/main/java/com/deepoove/swagger/diff/model/Endpoint.java index d31d49d9..f510b330 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/Endpoint.java +++ b/src/main/java/com/deepoove/swagger/diff/model/Endpoint.java @@ -3,54 +3,16 @@ import io.swagger.models.HttpMethod; import io.swagger.models.Operation; import io.swagger.models.Path; +import lombok.Data; +@Data public class Endpoint { - private String pathUrl; - private HttpMethod method; - private String summary; + private String pathUrl; + private HttpMethod method; + private String summary; - private Path path; - private Operation operation; - - public String getPathUrl() { - return pathUrl; - } - - public void setPathUrl(String pathUrl) { - this.pathUrl = pathUrl; - } - - public HttpMethod getMethod() { - return method; - } - - public void setMethod(HttpMethod method) { - this.method = method; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public Path getPath() { - return path; - } - - public void setPath(Path path) { - this.path = path; - } - - public Operation getOperation() { - return operation; - } - - public void setOperation(Operation operation) { - this.operation = operation; - } + private Path path; + private Operation operation; } diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index 5a841201..b291525f 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -1,27 +1,31 @@ package com.deepoove.swagger.diff.output; +import static j2html.TagCreator.a; import static j2html.TagCreator.body; +import static j2html.TagCreator.br; import static j2html.TagCreator.del; import static j2html.TagCreator.div; -import static j2html.TagCreator.rawHtml; import static j2html.TagCreator.document; +import static j2html.TagCreator.footer; import static j2html.TagCreator.h1; import static j2html.TagCreator.h2; import static j2html.TagCreator.h3; import static j2html.TagCreator.head; import static j2html.TagCreator.header; import static j2html.TagCreator.hr; -import static j2html.TagCreator.script; -import static j2html.TagCreator.a; import static j2html.TagCreator.html; +import static j2html.TagCreator.i; import static j2html.TagCreator.li; import static j2html.TagCreator.link; import static j2html.TagCreator.meta; import static j2html.TagCreator.ol; +import static j2html.TagCreator.rawHtml; +import static j2html.TagCreator.script; import static j2html.TagCreator.span; import static j2html.TagCreator.title; import static j2html.TagCreator.ul; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -37,190 +41,297 @@ import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; import j2html.tags.ContainerTag; +import j2html.tags.DomContent; +import j2html.tags.EmptyTag; public class HtmlRender implements Render { - - private String title; - private String linkCss; - - public HtmlRender() { - this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); - } - public HtmlRender(String title, String linkCss) { - this.title = title; - this.linkCss = linkCss; - } - - - public String render(SwaggerDiff diff) { - List newEndpoints = diff.getNewEndpoints(); - ContainerTag ol_newEndpoint = ol_newEndpoint(newEndpoints); - - List missingEndpoints = diff.getMissingEndpoints(); - ContainerTag ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); - - List changedEndpoints = diff.getChangedEndpoints(); - ContainerTag ol_changed = ol_changed(changedEndpoints); - - return reanderHtml(ol_newEndpoint, ol_missingEndpoint, ol_changed); - } - - public String reanderHtml(ContainerTag ol_new, ContainerTag ol_miss, ContainerTag ol_changed){ - ContainerTag html = html().attr("lang", "en").with( - head().with( - meta().withCharset("utf-8"), - title(title), - script(rawHtml("function showHide(id){if(document.getElementById(id).style.display==\'none\'){document.getElementById(id).style.display=\'block\';document.getElementById(\'btn_\'+id).innerHTML=\'⇑\';}else{document.getElementById(id).style.display=\'none\';document.getElementById(\'btn_\'+id).innerHTML=\'⇓\';}return true;}")).withType("text/javascript"), - link().withRel("stylesheet").withHref(linkCss) - ), - body().with( - header().with(h1(title)), - div().withClass("article").with( - div_headArticle("What's New", "new", ol_new), - div_headArticle("What's Deprecated", "deprecated", ol_miss), - div_headArticle("What's Changed", "changed", ol_changed) - ) - ) - ); - - return document().render() + html.render(); - } - - private ContainerTag div_headArticle(final String title, final String type, final ContainerTag ol) { - return div().with(h2(title).with(a(rawHtml("⇑")).withId("btn_" + type).withClass("showhide").withHref("#").attr("onClick", "javascript:showHide('" + type + "');")), hr(), ol); - } - - private ContainerTag ol_newEndpoint(List endpoints) { - if (null == endpoints) return ol().withId("new"); - ContainerTag ol = ol().withId("new"); - for (Endpoint endpoint : endpoints) { - ol.with(li_newEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return ol; - } - - private ContainerTag li_newEndpoint(String method, String path, - String desc) { - return li().with(span(method).withClass(method)).withText(path + " ") - .with(span(null == desc ? "" : desc)); - } - - private ContainerTag ol_missingEndpoint(List endpoints) { - if (null == endpoints) return ol().withId("deprecated"); - ContainerTag ol = ol().withId("deprecated"); - for (Endpoint endpoint : endpoints) { - ol.with(li_missingEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return ol; - } - - private ContainerTag li_missingEndpoint(String method, String path, - String desc) { - return li().with(span(method).withClass(method), - del().withText(path)).with(span(null == desc ? "" : " " + desc)); - } - - private ContainerTag ol_changed(List changedEndpoints){ - if (null == changedEndpoints) return ol().withId("changed"); - ContainerTag ol = ol().withId("changed"); - for (ChangedEndpoint changedEndpoint:changedEndpoints){ - String pathUrl = changedEndpoint.getPathUrl(); - Map changedOperations = changedEndpoint.getChangedOperations(); - for (Entry entry : changedOperations.entrySet()){ - String method = entry.getKey().toString(); - ChangedOperation changedOperation = entry.getValue(); - String desc = changedOperation.getSummary(); - - ContainerTag ul_detail = ul().withClass("detail"); - if (changedOperation.isDiffParam()){ - ul_detail.with(li().with(h3("Parameter")).with(ul_param(changedOperation))); - } - if (changedOperation.isDiffProp()){ - ul_detail.with(li().with(h3("Return Type")).with(ul_response(changedOperation))); - } - ol.with(li().with(span(method).withClass(method)).withText(pathUrl + " ").with(span(null == desc ? "" : desc)) - .with(ul_detail)); - } - } - return ol; - } - - private ContainerTag ul_response(ChangedOperation changedOperation) { - List addProps = changedOperation.getAddProps(); - List delProps = changedOperation.getMissingProps(); - ContainerTag ul = ul().withClass("change response"); - for (ElProperty prop : addProps){ - ul.with(li_addProp(prop)); - } - for (ElProperty prop : delProps){ - ul.with(li_missingProp(prop)); - } - return ul; - } - - private ContainerTag li_missingProp(ElProperty prop) { - Property property = prop.getProperty(); - return li().withClass("missing").withText("Delete").with(del(prop.getEl())).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); - } - - private ContainerTag li_addProp(ElProperty prop) { - Property property = prop.getProperty(); - return li().withText("Add " + prop.getEl()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); - } - - private ContainerTag ul_param(ChangedOperation changedOperation) { - List addParameters = changedOperation.getAddParameters(); - List delParameters = changedOperation.getMissingParameters(); - List changedParameters = changedOperation.getChangedParameter(); - ContainerTag ul = ul().withClass("change param"); - for (Parameter param : addParameters){ - ul.with(li_addParam(param)); - } - for (ChangedParameter param : changedParameters){ - List increased = param.getIncreased(); - for (ElProperty prop : increased){ - ul.with(li_addProp(prop)); - } - } - for (ChangedParameter param : changedParameters){ - boolean changeRequired = param.isChangeRequired(); - boolean changeDescription = param.isChangeDescription(); - if (changeRequired || changeDescription) - ul.with(li_changedParam(param)); - } - for (ChangedParameter param : changedParameters){ - List missing = param.getMissing(); - for (ElProperty prop : missing){ - ul.with(li_missingProp(prop)); - } - } - for (Parameter param : delParameters){ - ul.with(li_missingParam(param)); - } - return ul; - } - - private ContainerTag li_addParam(Parameter param){ - return li().withText("Add " + param.getName()).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); - } - private ContainerTag li_missingParam(Parameter param){ - return li().withClass("missing").with(span("Delete")).with(del(param.getName())).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); - } - private ContainerTag li_changedParam(ChangedParameter changeParam){ - boolean changeRequired = changeParam.isChangeRequired(); - boolean changeDescription = changeParam.isChangeDescription(); - Parameter rightParam = changeParam.getRightParameter(); - Parameter leftParam = changeParam.getLeftParameter(); - ContainerTag li = li().withText(rightParam.getName()); - if (changeRequired){ - li.withText(" change into " + (rightParam.getRequired() ? "required" : "not required")); - } - if (changeDescription){ - li.withText(" Notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); - } - return li; - } + + private static final String NON_BACKWARDS_CHANGES = "Non-backwards changes"; + private final String title; + private final List cssLinks; + private final List scriptsJsLinks; + private boolean showBackwardsIncompatibilities; + + public HtmlRender() { + this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); + } + public HtmlRender(final String title, final String linkCss) { + this.title = title; + cssLinks = new ArrayList(); + scriptsJsLinks = new ArrayList(); + cssLinks.add(linkCss); // Keep backward + } + + /** + * @param pTitle : page's title + * @param pListLinkCss : list of Css links + * @param pListLinkScritJs : list of JS Scripts links + */ + public HtmlRender(final String pTitle, final List pCssLinks, final List pScriptsJsLinks) { + super(); + title = pTitle; + cssLinks = pCssLinks; + scriptsJsLinks = pScriptsJsLinks; + } + + @Override + public String render(final SwaggerDiff diff) { + List newEndpoints = diff.getNewEndpoints(); + ContainerTag ol_newEndpoint = ol_newEndpoint(newEndpoints); + + List missingEndpoints = diff.getMissingEndpoints(); + ContainerTag ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); + + List changedEndpoints = diff.getChangedEndpoints(); + ContainerTag ol_changed = ol_changed(changedEndpoints); + + + return reanderHtml(ol_newEndpoint, ol_missingEndpoint, ol_changed); + } + + public String reanderHtml(final ContainerTag ol_new, final ContainerTag ol_miss, final ContainerTag ol_changed){ + List cssLinksTags = new ArrayList(); + if(!cssLinks.isEmpty()) { + for (String cssLink : cssLinks) { + cssLinksTags.add(link().withRel("stylesheet").withHref(cssLink)); + } + } + + List cssScriptsJsTags = new ArrayList(); + if(!scriptsJsLinks.isEmpty()){ + for (String scriptJs : scriptsJsLinks) { + cssScriptsJsTags.add(script().withType("text/javascript").withSrc(scriptJs)); + } + } + + // Build articles + List articles = new ArrayList(); + if(showBackwardsIncompatibilities) { + articles.add(span().with(i_backwardsIncompatibilitiesWarning(),span().withText(" " + NON_BACKWARDS_CHANGES )).withStyle("float:right")); + articles.add(br()); + } + articles.add( div_headArticle("What's New", "new", ol_new)); + articles.add(div_headArticle("What's Missed", "missed", ol_miss)); + articles.add(div_headArticle("What's Changed", "changed", ol_changed)); + + ContainerTag html = html().attr("lang", "en").with( + head().with( + meta().withCharset("utf-8"), + title(title), + script(rawHtml("function showHide(id){if(document.getElementById(id).style.display==\'none\'){document.getElementById(id).style.display=\'block\';document.getElementById(\'btn_\'+id).innerHTML=\'⇑\';}else{document.getElementById(id).style.display=\'none\';document.getElementById(\'btn_\'+id).innerHTML=\'⇓\';}return true;}")).withType("text/javascript") + ).with(cssLinksTags), + body().with( + header().with(h1(title)), + div().withClass("article").with(articles) + ), + footer().with(cssScriptsJsTags) + ); + + return document().render() + html.render(); + } + + public HtmlRender withBackwardsIncompatibilities() { + showBackwardsIncompatibilities = true; + return this; + } + + private ContainerTag div_headArticle(final String title, final String type, final ContainerTag ol) { + return div().with(h2(title).with(a(rawHtml("⇑")).withId("btn_" + type).withClass("showhide").withHref("#").attr("onClick", "javascript:showHide('" + type + "');")), hr(), ol); + } + + private ContainerTag ol_newEndpoint(final List endpoints) { + if (null == endpoints) { + return ol().withId("new"); + } + ContainerTag ol = ol().withId("new"); + for (Endpoint endpoint : endpoints) { + ol.with(li_newEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return ol; + } + + private ContainerTag li_newEndpoint(final String method, final String path, + final String desc) { + return li().with(span(method).withClass(method)).withText(path) + .with(span(null == desc ? "" : " " + desc)); + } + + private ContainerTag ol_missingEndpoint(final List endpoints) { + if (null == endpoints) { + return ol().withId("missed"); + } + ContainerTag ol = ol().withId("missed"); + for (Endpoint endpoint : endpoints) { + ol.with(li_missingEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return ol; + } + + private ContainerTag li_missingEndpoint(final String method, final String path, + final String desc) { + return li().with(span(method).withClass(method), + del().withText(path)).with(i_backwardsIncompatibilitiesWarning()).with(span(null == desc ? "" : " " + desc)); + } + + private ContainerTag ol_changed(final List changedEndpoints){ + if (null == changedEndpoints) { + return ol().withId("changed"); + } + ContainerTag ol = ol().withId("changed"); + for (ChangedEndpoint changedEndpoint:changedEndpoints){ + String pathUrl = changedEndpoint.getPathUrl(); + Map changedOperations = changedEndpoint.getChangedOperations(); + for (Entry entry : changedOperations.entrySet()){ + String method = entry.getKey().toString(); + ChangedOperation changedOperation = entry.getValue(); + String desc = changedOperation.getSummary(); + + ContainerTag ul_detail = ul().withClass("detail"); + if (changedOperation.isDiffParam()){ + ul_detail.with(li().with(h3("Parameter")).with(ul_param(changedOperation))); + } + if (changedOperation.isDiffProp()){ + ul_detail.with(li().with(h3("Return Type")).with(ul_response(changedOperation))); + } + ContainerTag li = li(); + li.with(span(method).withClass(method)).withText(pathUrl); + if(!changedEndpoint.isBackwardsCompatible()) { + li.with(i_backwardsIncompatibilitiesWarning()); + } + li.with(span(null == desc ? "" : " " + desc)).with(ul_detail); + ol.with(li); + } + } + return ol; + } + + private ContainerTag ul_response(final ChangedOperation changedOperation) { + List addProps = changedOperation.getAddProps(); + List delProps = changedOperation.getMissingProps(); + ListchangProps = changedOperation.getChangedProps(); + ContainerTag ul = ul().withClass("change response"); + for (ElProperty prop : addProps){ + ul.with(li_addProp(prop)); + } + for (ElProperty prop : changProps){ + ul.with(li_changeTypeProp(prop)); + } + for (ElProperty prop : delProps){ + ul.with(li_missingProp(prop)); + } + return ul; + } + + private ContainerTag li_missingProp(final ElProperty prop) { + Property property = prop.getProperty(); + return li().withClass("missing").withText("Delete").with(del().with(textField(prop.getEl()))).with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("// " + property.getDescription())).withClass("comment")); + } + + private ContainerTag li_addProp(final ElProperty prop) { + Property property = prop.getProperty(); + ContainerTag li = li().withText("Add ").with(textField(prop.getEl())); + if(prop.getProperty()!= null && prop.getProperty().getRequired()) { + li.withText(" required").with(i_backwardsIncompatibilitiesWarning()); + } + return li.with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); + } + + private ContainerTag li_changeTypeProp(final ElProperty prop) { + Property property = prop.getProperty(); + return li().withText("Change type ").with(textField(prop.getEl())).with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); + } + + private ContainerTag textField(final String pField) { + return span().withText(pField).withClass("field"); + } + + private ContainerTag li_changeRequiredProp(final ElProperty prop) { + Property property = prop.getProperty(); + return li().with(textField(prop.getEl())).withText(" change into required").with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); + } + + private ContainerTag ul_param(final ChangedOperation changedOperation) { + List addParameters = changedOperation.getAddParameters(); + List delParameters = changedOperation.getMissingParameters(); + List changedParameters = changedOperation.getChangedParameters(); + ContainerTag ul = ul().withClass("change param"); + for (Parameter param : addParameters){ + ul.with(li_addParam(param)); + } + for (ChangedParameter param : changedParameters){ + List increased = param.getIncreased(); + for (ElProperty prop : increased){ + ul.with(li_addProp(prop)); + } + } + for (ChangedParameter param : changedParameters){ + List requiredChanges = param.getRequiredChanges(); + for (ElProperty prop : requiredChanges){ + ul.with(li_changeRequiredProp(prop)); + } + } + for (ChangedParameter param : changedParameters){ + List typesChanges = param.getTypesChanges(); + for (ElProperty prop : typesChanges){ + ul.with(li_changeTypeProp(prop)); + } + } + for (ChangedParameter param : changedParameters){ + boolean changeRequired = param.isChangeRequired(); + boolean changeDescription = param.isChangeDescription(); + if (changeRequired || changeDescription) { + ul.with(li_changedParam(param)); + } + } + for (ChangedParameter param : changedParameters){ + List missing = param.getMissing(); + for (ElProperty prop : missing){ + ul.with(li_missingProp(prop)); + } + } + for (Parameter param : delParameters){ + ul.with(li_missingParam(param)); + } + return ul; + } + + private ContainerTag li_addParam(final Parameter param){ + ContainerTag li = li(); + if(param.getRequired()) { + li.withText("Add required ").with(textField(param.getName())).with(i_backwardsIncompatibilitiesWarning()); + } else { + li.withText("Add ").with(textField(param.getName())); + } + return li.with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); + } + private ContainerTag li_missingParam(final Parameter param){ + return li().withClass("missing").with(span("Delete")).with(del(textField(param.getName()))).with(i_backwardsIncompatibilitiesWarning()).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); + } + private ContainerTag li_changedParam(final ChangedParameter changeParam){ + boolean changeRequired = changeParam.isChangeRequired(); + boolean changeDescription = changeParam.isChangeDescription(); + Parameter rightParam = changeParam.getRightParameter(); + Parameter leftParam = changeParam.getLeftParameter(); + ContainerTag li = li().with(textField(rightParam.getName())); + if (changeRequired){ + li.withText(" change into " + (rightParam.getRequired() ? "required " : "not required ")); + } + if (changeDescription){ + li.withText(" Notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); + } + if(!changeParam.isBackwardsCompatible()) { + li.with(i_backwardsIncompatibilitiesWarning()); + } + return li; + } + + /** Add icon if modifications make backwards incompatibilies. */ + private ContainerTag i_backwardsIncompatibilitiesWarning() { + return showBackwardsIncompatibilities ? i().withClass("fas fa-fire text-warning").withStyle("padding-left:5px;padding-right:5px").withTitle(NON_BACKWARDS_CHANGES):null; + } + + } diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index 22b04dc9..9fd78364 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -8,8 +8,8 @@ import com.deepoove.swagger.diff.model.ChangedEndpoint; import com.deepoove.swagger.diff.model.ChangedOperation; import com.deepoove.swagger.diff.model.ChangedParameter; -import com.deepoove.swagger.diff.model.Endpoint; import com.deepoove.swagger.diff.model.ElProperty; +import com.deepoove.swagger.diff.model.Endpoint; import io.swagger.models.HttpMethod; import io.swagger.models.parameters.Parameter; @@ -17,198 +17,207 @@ public class MarkdownRender implements Render { - final String H3 = "### "; - final String BLOCKQUOTE = "> "; - final String CODE = "`"; - final String PRE_CODE = " "; - final String PRE_LI = " "; - final String LI = "* "; - final String HR = "---\n"; - - public MarkdownRender() {} - - public String render(SwaggerDiff diff) { - List newEndpoints = diff.getNewEndpoints(); - String ol_newEndpoint = ol_newEndpoint(newEndpoints); - - List missingEndpoints = diff.getMissingEndpoints(); - String ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); - - List changedEndpoints = diff.getChangedEndpoints(); - String ol_changed = ol_changed(changedEndpoints); - - return reanderHtml(ol_newEndpoint, ol_missingEndpoint, ol_changed); - } - - public String reanderHtml(String ol_new, String ol_miss, - String ol_changed) { - StringBuffer sb = new StringBuffer(); - sb.append(H3).append("What's New").append("\n").append(HR) - .append(ol_new).append("\n").append(H3) - .append("What's Deprecated").append("\n").append(HR) - .append(ol_miss).append("\n").append(H3) - .append("What's Changed").append("\n").append(HR) - .append(ol_changed); - return sb.toString(); - } - - private String ol_newEndpoint(List endpoints) { - if (null == endpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (Endpoint endpoint : endpoints) { - sb.append(li_newEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return sb.toString(); - } - - private String li_newEndpoint(String method, String path, String desc) { - StringBuffer sb = new StringBuffer(); - sb.append(LI).append(CODE).append(method).append(CODE) - .append(" " + path).append(" " + desc + "\n"); - return sb.toString(); - } - - private String ol_missingEndpoint(List endpoints) { - if (null == endpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (Endpoint endpoint : endpoints) { - sb.append(li_newEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return sb.toString(); - } - - private String ol_changed(List changedEndpoints) { - if (null == changedEndpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (ChangedEndpoint changedEndpoint : changedEndpoints) { - String pathUrl = changedEndpoint.getPathUrl(); - Map changedOperations = changedEndpoint - .getChangedOperations(); - for (Entry entry : changedOperations - .entrySet()) { - String method = entry.getKey().toString(); - ChangedOperation changedOperation = entry.getValue(); - String desc = changedOperation.getSummary(); - - StringBuffer ul_detail = new StringBuffer(); - if (changedOperation.isDiffParam()) { - ul_detail.append(PRE_LI).append("Parameter") - .append(ul_param(changedOperation)); - } - if (changedOperation.isDiffProp()) { - ul_detail.append(PRE_LI).append("Return Type") - .append(ul_response(changedOperation)); - } - sb.append(LI).append(CODE).append(method).append(CODE) - .append(" " + pathUrl).append(" " + desc + " \n") - .append(ul_detail); - } - } - return sb.toString(); - } - - private String ul_response(ChangedOperation changedOperation) { - List addProps = changedOperation.getAddProps(); - List delProps = changedOperation.getMissingProps(); - StringBuffer sb = new StringBuffer("\n\n"); - for (ElProperty prop : addProps) { - sb.append(PRE_LI).append(PRE_CODE).append(li_addProp(prop) + "\n"); - } - for (ElProperty prop : delProps) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_missingProp(prop) + "\n"); - } - return sb.toString(); - } - - private String li_missingProp(ElProperty prop) { - Property property = prop.getProperty(); - StringBuffer sb = new StringBuffer(""); - sb.append("Delete ").append(prop.getEl()) - .append(null == property.getDescription() ? "" - : (" //" + property.getDescription())); - return sb.toString(); - } - - private String li_addProp(ElProperty prop) { - Property property = prop.getProperty(); - StringBuffer sb = new StringBuffer(""); - sb.append("Add ").append(prop.getEl()) - .append(null == property.getDescription() ? "" - : (" //" + property.getDescription())); - return sb.toString(); - } - - private String ul_param(ChangedOperation changedOperation) { - List addParameters = changedOperation.getAddParameters(); - List delParameters = changedOperation.getMissingParameters(); - List changedParameters = changedOperation - .getChangedParameter(); - StringBuffer sb = new StringBuffer("\n\n"); - for (Parameter param : addParameters) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_addParam(param) + "\n"); - } - for (ChangedParameter param : changedParameters) { - List increased = param.getIncreased(); - for (ElProperty prop : increased) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_addProp(prop) + "\n"); - } - } - for (ChangedParameter param : changedParameters) { - boolean changeRequired = param.isChangeRequired(); - boolean changeDescription = param.isChangeDescription(); - if (changeRequired || changeDescription) sb.append(PRE_LI) - .append(PRE_CODE).append(li_changedParam(param) + "\n"); - } - for (ChangedParameter param : changedParameters) { - List missing = param.getMissing(); - for (ElProperty prop : missing) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_missingProp(prop) + "\n"); - } - } - for (Parameter param : delParameters) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_missingParam(param) + "\n"); - } - return sb.toString(); - } - - private String li_addParam(Parameter param) { - StringBuffer sb = new StringBuffer(""); - sb.append("Add ").append(param.getName()) - .append(null == param.getDescription() ? "" - : (" //" + param.getDescription())); - return sb.toString(); - } - - private String li_missingParam(Parameter param) { - StringBuffer sb = new StringBuffer(""); - sb.append("Delete ").append(param.getName()) - .append(null == param.getDescription() ? "" - : (" //" + param.getDescription())); - return sb.toString(); - } - - private String li_changedParam(ChangedParameter changeParam) { - boolean changeRequired = changeParam.isChangeRequired(); - boolean changeDescription = changeParam.isChangeDescription(); - Parameter rightParam = changeParam.getRightParameter(); - Parameter leftParam = changeParam.getLeftParameter(); - StringBuffer sb = new StringBuffer(""); - sb.append(rightParam.getName()); - if (changeRequired) { - sb.append(" change into " + (rightParam.getRequired() ? "required" : "not required")); - } - if (changeDescription) { - sb.append(" Notes ").append(leftParam.getDescription()).append(" change into ") - .append(rightParam.getDescription()); - } - return sb.toString(); - } + final String H3 = "### "; + final String BLOCKQUOTE = "> "; + final String CODE = "`"; + final String PRE_CODE = " "; + final String PRE_LI = " "; + final String LI = "* "; + final String HR = "---\n"; + + public MarkdownRender() {} + + @Override + public String render(final SwaggerDiff diff) { + List newEndpoints = diff.getNewEndpoints(); + String ol_newEndpoint = ol_newEndpoint(newEndpoints); + + List missingEndpoints = diff.getMissingEndpoints(); + String ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); + + List changedEndpoints = diff.getChangedEndpoints(); + String ol_changed = ol_changed(changedEndpoints); + + return reanderHtml(ol_newEndpoint, ol_missingEndpoint, ol_changed); + } + + public String reanderHtml(final String ol_new, final String ol_miss, + final String ol_changed) { + StringBuffer sb = new StringBuffer(); + sb.append(H3).append("What's New").append("\n").append(HR) + .append(ol_new).append("\n").append(H3) + .append("What's Deprecated").append("\n").append(HR) + .append(ol_miss).append("\n").append(H3) + .append("What's Changed").append("\n").append(HR) + .append(ol_changed); + return sb.toString(); + } + + private String ol_newEndpoint(final List endpoints) { + if (null == endpoints) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (Endpoint endpoint : endpoints) { + sb.append(li_newEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return sb.toString(); + } + + private String li_newEndpoint(final String method, final String path, final String desc) { + StringBuffer sb = new StringBuffer(); + sb.append(LI).append(CODE).append(method).append(CODE) + .append(" " + path).append(" " + desc + "\n"); + return sb.toString(); + } + + private String ol_missingEndpoint(final List endpoints) { + if (null == endpoints) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (Endpoint endpoint : endpoints) { + sb.append(li_newEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return sb.toString(); + } + + private String ol_changed(final List changedEndpoints) { + if (null == changedEndpoints) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (ChangedEndpoint changedEndpoint : changedEndpoints) { + String pathUrl = changedEndpoint.getPathUrl(); + Map changedOperations = changedEndpoint + .getChangedOperations(); + for (Entry entry : changedOperations + .entrySet()) { + String method = entry.getKey().toString(); + ChangedOperation changedOperation = entry.getValue(); + String desc = changedOperation.getSummary(); + + StringBuffer ul_detail = new StringBuffer(); + if (changedOperation.isDiffParam()) { + ul_detail.append(PRE_LI).append("Parameter") + .append(ul_param(changedOperation)); + } + if (changedOperation.isDiffProp()) { + ul_detail.append(PRE_LI).append("Return Type") + .append(ul_response(changedOperation)); + } + sb.append(LI).append(CODE).append(method).append(CODE) + .append(" " + pathUrl).append(" " + desc + " \n") + .append(ul_detail); + } + } + return sb.toString(); + } + + private String ul_response(final ChangedOperation changedOperation) { + List addProps = changedOperation.getAddProps(); + List delProps = changedOperation.getMissingProps(); + StringBuffer sb = new StringBuffer("\n\n"); + for (ElProperty prop : addProps) { + sb.append(PRE_LI).append(PRE_CODE).append(li_addProp(prop) + "\n"); + } + for (ElProperty prop : delProps) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_missingProp(prop) + "\n"); + } + return sb.toString(); + } + + private String li_missingProp(final ElProperty prop) { + Property property = prop.getProperty(); + StringBuffer sb = new StringBuffer(""); + sb.append("Delete ").append(prop.getEl()) + .append(null == property.getDescription() ? "" + : (" //" + property.getDescription())); + return sb.toString(); + } + + private String li_addProp(final ElProperty prop) { + Property property = prop.getProperty(); + StringBuffer sb = new StringBuffer(""); + sb.append("Add ").append(prop.getEl()) + .append(null == property.getDescription() ? "" + : (" //" + property.getDescription())); + return sb.toString(); + } + + private String ul_param(final ChangedOperation changedOperation) { + List addParameters = changedOperation.getAddParameters(); + List delParameters = changedOperation.getMissingParameters(); + List changedParameters = changedOperation + .getChangedParameters(); + StringBuffer sb = new StringBuffer("\n\n"); + for (Parameter param : addParameters) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_addParam(param) + "\n"); + } + for (ChangedParameter param : changedParameters) { + List increased = param.getIncreased(); + for (ElProperty prop : increased) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_addProp(prop) + "\n"); + } + } + for (ChangedParameter param : changedParameters) { + boolean changeRequired = param.isChangeRequired(); + boolean changeDescription = param.isChangeDescription(); + if (changeRequired || changeDescription) { + sb.append(PRE_LI) + .append(PRE_CODE).append(li_changedParam(param) + "\n"); + } + } + for (ChangedParameter param : changedParameters) { + List missing = param.getMissing(); + for (ElProperty prop : missing) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_missingProp(prop) + "\n"); + } + } + for (Parameter param : delParameters) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_missingParam(param) + "\n"); + } + return sb.toString(); + } + + private String li_addParam(final Parameter param) { + StringBuffer sb = new StringBuffer(""); + sb.append("Add ").append(param.getName()) + .append(null == param.getDescription() ? "" + : (" //" + param.getDescription())); + return sb.toString(); + } + + private String li_missingParam(final Parameter param) { + StringBuffer sb = new StringBuffer(""); + sb.append("Delete ").append(param.getName()) + .append(null == param.getDescription() ? "" + : (" //" + param.getDescription())); + return sb.toString(); + } + + private String li_changedParam(final ChangedParameter changeParam) { + boolean changeRequired = changeParam.isChangeRequired(); + boolean changeDescription = changeParam.isChangeDescription(); + Parameter rightParam = changeParam.getRightParameter(); + Parameter leftParam = changeParam.getLeftParameter(); + StringBuffer sb = new StringBuffer(""); + sb.append(rightParam.getName()); + if (changeRequired) { + sb.append(" change into " + (rightParam.getRequired() ? "required" : "not required")); + } + if (changeDescription) { + sb.append(" Notes ").append(leftParam.getDescription()).append(" change into ") + .append(rightParam.getDescription()); + } + return sb.toString(); + } } diff --git a/src/main/resources/demo.css b/src/main/resources/demo.css index 068b6371..b480f384 100644 --- a/src/main/resources/demo.css +++ b/src/main/resources/demo.css @@ -102,6 +102,11 @@ h3 { margin-top: 80px; } +.article .field { + font-style: italic; + color: #8cc5c0; +} + .article>div { padding: 10px 10px; } diff --git a/src/main/resources/template.html b/src/main/resources/template.html deleted file mode 100644 index 1e6b3f08..00000000 --- a/src/main/resources/template.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - -Api changelog - - - - -
-

Api change log

-
-
-
-

What's New

-
-
- -
-

What's Deprecated

-
-
- -
-

What's Changed

-
-
-
- - \ No newline at end of file diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java new file mode 100644 index 00000000..77c8347a --- /dev/null +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java @@ -0,0 +1,147 @@ +package com.deepoove.swagger.test; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import com.deepoove.swagger.diff.SwaggerDiff; +import com.deepoove.swagger.diff.model.ChangedEndpoint; +import com.deepoove.swagger.diff.output.HtmlRender; + +public class SwaggerDiffBackwardsIncompatibilitiesTest { + + private static final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; + + private static final String SWAGGER_V2_WITH_ALL_BACKWARDS_INCOMPATIBILITIES_DOC = "backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json"; + + private static final String SWAGGER_V2_WITH_ADD_REQUIRED_PARAM_DOC = "backwards_incompatibilities/petstore_v2_withAddRequiredParam.json"; + + private static final String SWAGGER_V2_WITH_DELETE_PARAM_DOC = "backwards_incompatibilities/petstore_v2_withDeleteParam.json"; + + private static final String SWAGGER_V2_WITH_DELETE_PROPERTY_DOC = "backwards_incompatibilities/petstore_v2_withDeletePropertyResponse.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_PARAM_TO_REQUIRED_DOC = "backwards_incompatibilities/petstore_v2_withChangeParamToRequired.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_PROPERTY_BODY_TO_REQUIRED_DOC = "backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_TYPE_PROPERTY_IN_BODY_DOC = "backwards_incompatibilities/petstore_v2_withTypePropertyBody.json"; + + private static final String SWAGGER_V2_WITH_ADD_REQUIRED_PROPERTY_IN_BODY_DOC = "backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_TYPE_PROPERTY_IN_RESPONSE_DOC = "backwards_incompatibilities/petstore_v2_withChangeTypePropertyResponse.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_TYPE_PATH_PARAM_DOC = "backwards_incompatibilities/petstore_v2_withChangeTypePathParam.json"; + + private static final String SWAGGER_V2_WITH_CHANGE_TYPE_QUERY_PARAM_DOC = "backwards_incompatibilities/petstore_v2_withChangeTypeQueryParam.json"; + + private static final String SWAGGER_V2_WITH_CHANGES_COMPATIBLES_DOC = "backwards_incompatibilities/petstore_v2_withChangesCompatibles.json"; + + @Test + public void testBackwardsIncompatibilitiesDeleteParam() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_DELETE_PARAM_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesAddRequiredParam() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_ADD_REQUIRED_PARAM_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesDeleteProperty() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_DELETE_PROPERTY_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesChangeParamToRequired() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_PARAM_TO_REQUIRED_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesChangePropertyBodyToRequired() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_PROPERTY_BODY_TO_REQUIRED_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesChangeTypePropertyBody() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_PROPERTY_IN_BODY_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesAddRequiredPropertyBody() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_ADD_REQUIRED_PROPERTY_IN_BODY_DOC); + } + + @Test + public void testBackwardsIncompatibilitiesChangeTypePropertyResponse() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_PROPERTY_IN_RESPONSE_DOC); + } + + @Test + @Ignore + public void testBackwardsIncompatibilitiesChangeTypeQueryParam() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_QUERY_PARAM_DOC); + // TODO JLA not implemented + } + + @Test + @Ignore + public void testBackwardsIncompatibilitiesChangeTypePathParam() { + verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_PATH_PARAM_DOC); + // TODO JLA not implemented + } + + @Test + public void testBackwardsCompatibles() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_WITH_CHANGES_COMPATIBLES_DOC); + buildRenderHtml(diff, "testBackwardsCompatibles"); + Assert.assertTrue(diff.isBackwardsCompatible()); + Assert.assertFalse(diff.getChangedEndpoints().isEmpty()); + Assert.assertFalse(diff.getNewEndpoints().isEmpty()); + } + + // TODO faire les tests avec cas retro, add new path + add attribute + add param non requis + etc. + + @Test + public void testAllBackwardsIncompatibilities() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_WITH_ALL_BACKWARDS_INCOMPATIBILITIES_DOC); + buildRenderHtml(diff, "testBackwardsAllIncompatibilities"); + List changedEndPoints = diff.getChangedEndpoints(); + Assert.assertFalse(diff.isBackwardsCompatible()); + Assert.assertFalse(changedEndPoints.isEmpty()); + + } + + private SwaggerDiff verifyBackwardsIncompatibilities(final String pNewSpec) { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, pNewSpec); + List changedEndPoints = diff.getChangedEndpoints(); + Assert.assertFalse("Contract must be incompatible.", diff.isBackwardsCompatible()); + Assert.assertFalse(changedEndPoints.isEmpty()); + return diff; + } + + private void buildRenderHtml(final SwaggerDiff pSwaggerDiff, final String pFileName) { + List css = new ArrayList(); + css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"); + css.add("https://use.fontawesome.com/releases/v5.0.6/css/all.css"); + css.add("file:///Users/lamaille/pagesjaunes/git/externe/swagger-diff/src/main/resources/demo.css"); // TODO à nettoyer + List scripts = new ArrayList(); +// css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"); + String html = new HtmlRender("Changelog", css, scripts).withBackwardsIncompatibilities() + .render(pSwaggerDiff); + try { + FileWriter fw = new FileWriter( + pFileName + ".html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredParam.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredParam.json new file mode 100644 index 00000000..62e8ae85 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredParam.json @@ -0,0 +1,1001 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + }, + { + "name": "name", + "in": "query", + "description": "Name that need to be considered for filter", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json new file mode 100644 index 00000000..2b09a1e9 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json @@ -0,0 +1,999 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls", + "num" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "num": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json new file mode 100644 index 00000000..675874af --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json @@ -0,0 +1,949 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + }, + { + "name": "name", + "in": "query", + "description": "Name that need to be considered for filter", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": true, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls", + "id", + "num" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "num": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "string", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "array" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeParamToRequired.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeParamToRequired.json new file mode 100644 index 00000000..dc546ac1 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeParamToRequired.json @@ -0,0 +1,994 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": true, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json new file mode 100644 index 00000000..dba27e60 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json @@ -0,0 +1,996 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls", + "category", + "id" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePathParam.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePathParam.json new file mode 100644 index 00000000..7088ccb6 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePathParam.json @@ -0,0 +1,993 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePropertyResponse.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePropertyResponse.json new file mode 100644 index 00000000..23429be0 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypePropertyResponse.json @@ -0,0 +1,994 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "array" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypeQueryParam.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypeQueryParam.json new file mode 100644 index 00000000..dd767c1f --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangeTypeQueryParam.json @@ -0,0 +1,984 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json new file mode 100644 index 00000000..289bc899 --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json @@ -0,0 +1,1064 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + }, + { + "name": "id", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findBySomething": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by something", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + }, + { + "name": "id", + "in": "query", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withDeleteParam.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withDeleteParam.json new file mode 100644 index 00000000..c72ab3cd --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withDeleteParam.json @@ -0,0 +1,977 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withDeletePropertyResponse.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withDeletePropertyResponse.json new file mode 100644 index 00000000..c614947b --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withDeletePropertyResponse.json @@ -0,0 +1,991 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withTypePropertyBody.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withTypePropertyBody.json new file mode 100644 index 00000000..a301e17f --- /dev/null +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withTypePropertyBody.json @@ -0,0 +1,994 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": true, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "deprecated": true + } + }, + "/pet/{petId}": { + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean", + "default": false + } + }, + "xml": { + "name": "Order" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "string", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file From 36fc691ed359457090743a4449a099929e36c58b Mon Sep 17 00:00:00 2001 From: Julien LAMY Date: Wed, 7 Feb 2018 18:21:13 +0100 Subject: [PATCH 03/18] clean test & todo --- .../java/com/deepoove/swagger/diff/compare/ModelDiff.java | 4 ++-- .../test/SwaggerDiffBackwardsIncompatibilitiesTest.java | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java index 4673d2d7..e8d47c5d 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java @@ -68,13 +68,13 @@ public ModelDiff diff(final Model leftModel, final Model rightModel, final Strin Property right = rightProperties.get(key); // Check if type change if(left.getType() != null && right.getType() != null && !left.getType().equals(right.getType())) { - Map map = new HashMap(); // TODO JLA ? + Map map = new HashMap(); map.put(key, right); typeChanges.addAll(convert2ElPropertys(map, parentEl , true)); } // Check if property becomes required if(left.getRequired() != right.getRequired() && right.getRequired()) { - Map map = new HashMap(); // TODO JLA ? + Map map = new HashMap(); map.put(key, right); requiredChanges.addAll(convert2ElPropertys(map, parentEl , true)); } diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java index 77c8347a..acbfb93a 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java @@ -104,8 +104,6 @@ public void testBackwardsCompatibles() { Assert.assertFalse(diff.getNewEndpoints().isEmpty()); } - // TODO faire les tests avec cas retro, add new path + add attribute + add param non requis + etc. - @Test public void testAllBackwardsIncompatibilities() { SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_WITH_ALL_BACKWARDS_INCOMPATIBILITIES_DOC); @@ -128,7 +126,7 @@ private void buildRenderHtml(final SwaggerDiff pSwaggerDiff, final String pFileN List css = new ArrayList(); css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"); css.add("https://use.fontawesome.com/releases/v5.0.6/css/all.css"); - css.add("file:///Users/lamaille/pagesjaunes/git/externe/swagger-diff/src/main/resources/demo.css"); // TODO à nettoyer + css.add("http://deepoove.com/swagger-diff/stylesheets/demo.css"); List scripts = new ArrayList(); // css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"); String html = new HtmlRender("Changelog", css, scripts).withBackwardsIncompatibilities() From 29349f8c0e1d3cb40469cfe848838b55547ce85a Mon Sep 17 00:00:00 2001 From: Julien LAMY Date: Wed, 7 Feb 2018 21:31:49 +0100 Subject: [PATCH 04/18] Add check backwards incompabilities for changes type param --- .gitignore | 2 + .../swagger/diff/compare/MapKeyDiff.java | 90 ++++---- .../swagger/diff/compare/ParameterDiff.java | 28 +-- .../swagger/diff/model/ChangedParameter.java | 4 +- .../swagger/diff/output/HtmlRender.java | 23 +- ...gerDiffBackwardsIncompatibilitiesTest.java | 5 - .../swagger/test/SwaggerDiffTest.java | 207 +++++++++--------- ..._v2_withAllBackwardsIncompatibilities.json | 5 +- 8 files changed, 177 insertions(+), 187 deletions(-) diff --git a/.gitignore b/.gitignore index 437d08f8..ff536d7c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ hs_err_pid* *.html testDiff\.md + +*.swp diff --git a/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java index 4ca2e250..457020ec 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java @@ -6,60 +6,54 @@ import java.util.Map; import java.util.Map.Entry; +import lombok.Getter; + /** * compare two Maps by key * @author Sayi - * @version + * @version */ +@Getter public class MapKeyDiff { - private Map increased; - private Map missing; - private List sharedKey; - - private MapKeyDiff() { - this.sharedKey = new ArrayList(); - } - - public static MapKeyDiff diff(Map mapLeft, - Map mapRight) { - MapKeyDiff instance = new MapKeyDiff(); - if (null == mapLeft && null == mapRight) return instance; - if (null == mapLeft) { - instance.increased = mapRight; - return instance; - } - if (null == mapRight) { - instance.missing = mapLeft; - return instance; - } - instance.increased = new LinkedHashMap(mapRight); - instance.missing = new LinkedHashMap(); - for (Entry entry : mapLeft.entrySet()) { - K leftKey = entry.getKey(); - V leftValue = entry.getValue(); - if (mapRight.containsKey(leftKey)) { - instance.increased.remove(leftKey); - instance.sharedKey.add(leftKey); - - } else { - instance.missing.put(leftKey, leftValue); - } - - } - return instance; - } - - public Map getIncreased() { - return increased; - } - - public Map getMissing() { - return missing; - } + private Map increased; + private Map missing; + private final List sharedKey; + + private MapKeyDiff() { + this.sharedKey = new ArrayList(); + } + + public static MapKeyDiff diff(final Map mapLeft, + final Map mapRight) { + MapKeyDiff instance = new MapKeyDiff(); + if (null == mapLeft && null == mapRight) { + return instance; + } + if (null == mapLeft) { + instance.increased = mapRight; + return instance; + } + if (null == mapRight) { + instance.missing = mapLeft; + return instance; + } + instance.increased = new LinkedHashMap(mapRight); + instance.missing = new LinkedHashMap(); + for (Entry entry : mapLeft.entrySet()) { + K leftKey = entry.getKey(); + V leftValue = entry.getValue(); + if (mapRight.containsKey(leftKey)) { + instance.increased.remove(leftKey); + instance.sharedKey.add(leftKey); + + } else { + instance.missing.put(leftKey, leftValue); + } + + } + return instance; + } - public List getSharedKey() { - return sharedKey; - } } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java index aec496fa..27b1ca52 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java @@ -4,12 +4,14 @@ import java.util.List; import java.util.Map; +import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import com.deepoove.swagger.diff.model.ChangedParameter; import io.swagger.models.Model; import io.swagger.models.RefModel; +import io.swagger.models.parameters.AbstractSerializableParameter; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.Parameter; import lombok.Data; @@ -57,7 +59,7 @@ public ParameterDiff diff(List left, int index = index(instance.increased, name); if (-1 == index){ instance.missing.add(leftPara); - }else{ + } else { Parameter rightPara = instance.increased.get(index); instance.increased.remove(index); ChangedParameter changedParameter = new ChangedParameter(); @@ -83,27 +85,17 @@ public ParameterDiff diff(List left, } - //is required + //is becomes required boolean rightRequired = rightPara.getRequired(); boolean leftRequired = leftPara.getRequired(); changedParameter.setChangeRequired(leftRequired != rightRequired && rightRequired); - // TODO JLA -// //is change type -// if(rightPara.getName().contains("username")){ -// SerializableParameter s = (SerializableParameter) rightPara; -// System.out.println(rightPara.getDescription()); -// System.out.println(s.getName()); -// System.out.println(s.getIn()); -// System.out.println(s.getType()); -// } -// if(leftPara.getName().contains("username")){ -// SerializableParameter s = (SerializableParameter) leftPara; -// System.out.println(leftPara.getDescription()); -// System.out.println(s.getName()); -// System.out.println(s.getIn()); -// System.out.println(s.getType()); -// } + //is change type + if (ClassUtils.getAllSuperclasses(leftPara.getClass()).contains(AbstractSerializableParameter.class) + && ClassUtils.getAllSuperclasses(rightPara.getClass()).contains(AbstractSerializableParameter.class) + && !(((AbstractSerializableParameter) leftPara).getType()).equals(((AbstractSerializableParameter) rightPara).getType())) { + changedParameter.setChangeType(true); + } //description String description = rightPara.getDescription(); diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java index 337055c7..bee0586f 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java @@ -18,7 +18,7 @@ public class ChangedParameter implements Changed { private Parameter rightParameter; private boolean isChangeRequired; -// private boolean isChangeType; // TODO JLA hard ? + private boolean isChangeType; private boolean isChangeDescription; @Override @@ -27,6 +27,7 @@ public boolean isDiff() { || isChangeDescription || !increased.isEmpty() || !missing.isEmpty() + || isChangeType || !requiredChanges.isEmpty() || !typesChanges.isEmpty(); } @@ -34,6 +35,7 @@ public boolean isDiff() { @Override public boolean isBackwardsCompatible() { boolean isBackwardsCompatible = !isChangeRequired + && !isChangeType && missing.isEmpty() && requiredChanges.isEmpty() && typesChanges.isEmpty(); diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index b291525f..f781ca8a 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -240,7 +240,7 @@ private ContainerTag li_addProp(final ElProperty prop) { private ContainerTag li_changeTypeProp(final ElProperty prop) { Property property = prop.getProperty(); - return li().withText("Change type ").with(textField(prop.getEl())).with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); + return li().with(textField(prop.getEl())).withText(" changes type").with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); } private ContainerTag textField(final String pField) { @@ -279,9 +279,7 @@ private ContainerTag ul_param(final ChangedOperation changedOperation) { } } for (ChangedParameter param : changedParameters){ - boolean changeRequired = param.isChangeRequired(); - boolean changeDescription = param.isChangeDescription(); - if (changeRequired || changeDescription) { + if (param.isChangeRequired() || param.isChangeDescription() || param.isChangeType()) { ul.with(li_changedParam(param)); } } @@ -312,14 +310,21 @@ private ContainerTag li_missingParam(final Parameter param){ private ContainerTag li_changedParam(final ChangedParameter changeParam){ boolean changeRequired = changeParam.isChangeRequired(); boolean changeDescription = changeParam.isChangeDescription(); + boolean changeType = changeParam.isChangeType(); Parameter rightParam = changeParam.getRightParameter(); Parameter leftParam = changeParam.getLeftParameter(); ContainerTag li = li().with(textField(rightParam.getName())); - if (changeRequired){ - li.withText(" change into " + (rightParam.getRequired() ? "required " : "not required ")); - } - if (changeDescription){ - li.withText(" Notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); + if(changeRequired || changeType || changeDescription) { + li.withText(" : "); + if (changeRequired){ + li.with(br()).withText(" - becomes " + (rightParam.getRequired() ? "required " : "not required ")); + } + if (changeType) { + li.with(br()).withText(" - changes type "); + } + if (changeDescription){ + li.with(br()).withText(" - notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); + } } if(!changeParam.isBackwardsCompatible()) { li.with(i_backwardsIncompatibilitiesWarning()); diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java index acbfb93a..499e1e46 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java @@ -6,7 +6,6 @@ import java.util.List; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import com.deepoove.swagger.diff.SwaggerDiff; @@ -82,17 +81,13 @@ public void testBackwardsIncompatibilitiesChangeTypePropertyResponse() { } @Test - @Ignore public void testBackwardsIncompatibilitiesChangeTypeQueryParam() { verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_QUERY_PARAM_DOC); - // TODO JLA not implemented } @Test - @Ignore public void testBackwardsIncompatibilitiesChangeTypePathParam() { verifyBackwardsIncompatibilities(SWAGGER_V2_WITH_CHANGE_TYPE_PATH_PARAM_DOC); - // TODO JLA not implemented } @Test diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java index d6c2ce90..ecc3688b 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java @@ -15,108 +15,109 @@ public class SwaggerDiffTest { - final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; - final String SWAGGER_V2_DOC2 = "petstore_v2_2.json"; - final String SWAGGER_V2_EMPTY_DOC = "petstore_v2_empty.json"; - final String SWAGGER_V2_HTTP = "http://petstore.swagger.io/v2/swagger.json"; - - @Test - public void testEqual() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC2, SWAGGER_V2_DOC2); - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - Assert.assertTrue(newEndpoints.isEmpty()); - Assert.assertTrue(missingEndpoints.isEmpty()); - Assert.assertTrue(changedEndPoints.isEmpty()); - - } - - @Test - public void testNewApi() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_EMPTY_DOC, SWAGGER_V2_DOC2); - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testNewApi.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertTrue(newEndpoints.size() > 0); - Assert.assertTrue(missingEndpoints.isEmpty()); - Assert.assertTrue(changedEndPoints.isEmpty()); - - } - - @Test - public void testDeprecatedApi() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_EMPTY_DOC); - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testDeprecatedApi.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertTrue(newEndpoints.isEmpty()); - Assert.assertTrue(missingEndpoints.size() > 0); - Assert.assertTrue(changedEndPoints.isEmpty()); - - } - - @Test - public void testDiff() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testDiff.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertFalse(changedEndPoints.isEmpty()); - - } - - @Test - public void testDiffAndMarkdown() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - String render = new MarkdownRender().render(diff); - try { - FileWriter fw = new FileWriter( - "testDiff.md"); - fw.write(render); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - - } + final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; + final String SWAGGER_V2_DOC2 = "petstore_v2_2.json"; + final String SWAGGER_V2_EMPTY_DOC = "petstore_v2_empty.json"; + final String SWAGGER_V2_HTTP = "http://petstore.swagger.io/v2/swagger.json"; + + @Test + public void testEqual() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC2, SWAGGER_V2_DOC2); + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + Assert.assertTrue(newEndpoints.isEmpty()); + Assert.assertTrue(missingEndpoints.isEmpty()); + Assert.assertTrue(changedEndPoints.isEmpty()); + + } + + @Test + public void testNewApi() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_EMPTY_DOC, SWAGGER_V2_DOC2); + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testNewApi.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertTrue(newEndpoints.size() > 0); + Assert.assertTrue(missingEndpoints.isEmpty()); + Assert.assertTrue(changedEndPoints.isEmpty()); + + } + + @Test + public void testDeprecatedApi() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_EMPTY_DOC); + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testDeprecatedApi.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertTrue(newEndpoints.isEmpty()); + Assert.assertTrue(missingEndpoints.size() > 0); + Assert.assertTrue(changedEndPoints.isEmpty()); + Assert.assertFalse("Contract must be incompatible.", diff.isBackwardsCompatible()); + + } + + @Test + public void testDiff() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testDiff.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertFalse(changedEndPoints.isEmpty()); + + } + + @Test + public void testDiffAndMarkdown() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); + String render = new MarkdownRender().render(diff); + try { + FileWriter fw = new FileWriter( + "testDiff.md"); + fw.write(render); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + + } } diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json index 675874af..3f646623 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json @@ -207,8 +207,7 @@ "in": "path", "description": "ID of pet that needs to be updated", "required": true, - "type": "integer", - "format": "int64" + "type": "string" }, { "name": "name", @@ -599,7 +598,7 @@ "in": "query", "description": "The password for login in clear text", "required": true, - "type": "string" + "type": "integer" } ], "responses": { From ca1f6af740dd297eb511e1530c600837193ffbe9 Mon Sep 17 00:00:00 2001 From: Julien LAMY Date: Wed, 7 Feb 2018 22:03:17 +0100 Subject: [PATCH 05/18] Modify css icon warn backwards --- .../com/deepoove/swagger/diff/output/HtmlRender.java | 4 +++- src/main/resources/demo.css | 9 ++++++++- .../test/SwaggerDiffBackwardsIncompatibilitiesTest.java | 2 -- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index f781ca8a..da8d05a1 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -304,9 +304,11 @@ private ContainerTag li_addParam(final Parameter param){ } return li.with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); } + private ContainerTag li_missingParam(final Parameter param){ return li().withClass("missing").with(span("Delete")).with(del(textField(param.getName()))).with(i_backwardsIncompatibilitiesWarning()).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); } + private ContainerTag li_changedParam(final ChangedParameter changeParam){ boolean changeRequired = changeParam.isChangeRequired(); boolean changeDescription = changeParam.isChangeDescription(); @@ -334,7 +336,7 @@ private ContainerTag li_changedParam(final ChangedParameter changeParam){ /** Add icon if modifications make backwards incompatibilies. */ private ContainerTag i_backwardsIncompatibilitiesWarning() { - return showBackwardsIncompatibilities ? i().withClass("fas fa-fire text-warning").withStyle("padding-left:5px;padding-right:5px").withTitle(NON_BACKWARDS_CHANGES):null; + return showBackwardsIncompatibilities ? i().withClass("fas fa-exclamation-circle warnbackward").withTitle(NON_BACKWARDS_CHANGES):null; } diff --git a/src/main/resources/demo.css b/src/main/resources/demo.css index b480f384..cf94cf09 100644 --- a/src/main/resources/demo.css +++ b/src/main/resources/demo.css @@ -185,4 +185,11 @@ s, del, .missing, del.comment { padding: 2px; font-size: 12px; float: right; -} \ No newline at end of file +} + +/* Class for icon warning backwards */ +.warnbackward { + color:#e2c600; + padding-left:5px; + padding-right:5px; +} diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java index 499e1e46..fe3a211b 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java @@ -119,11 +119,9 @@ private SwaggerDiff verifyBackwardsIncompatibilities(final String pNewSpec) { private void buildRenderHtml(final SwaggerDiff pSwaggerDiff, final String pFileName) { List css = new ArrayList(); - css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"); css.add("https://use.fontawesome.com/releases/v5.0.6/css/all.css"); css.add("http://deepoove.com/swagger-diff/stylesheets/demo.css"); List scripts = new ArrayList(); -// css.add("https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"); String html = new HtmlRender("Changelog", css, scripts).withBackwardsIncompatibilities() .render(pSwaggerDiff); try { From 433b478a7dc9d972c9cb9c8c22a4488a9746f619 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Mon, 29 Jun 2020 10:06:30 +0200 Subject: [PATCH 06/18] Fix merge on unit tests --- .../diff/compare/SpecificationDiff.java | 2 +- .../petstore_v2_withChangesCompatibles.json | 60 ++++++++++--------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java index e3c6854d..4540efba 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java @@ -78,7 +78,7 @@ public static SpecificationDiff diff(Swagger oldSpec, Swagger newSpec) { .diff(oldParameters, newParameters); changedOperation.setAddParameters(parameterDiff.getIncreased()); changedOperation.setMissingParameters(parameterDiff.getMissing()); - changedOperation.setChangedParameter(parameterDiff.getChanged()); + changedOperation.setChangedParameters(parameterDiff.getChanged()); // Diff response Property oldResponseProperty = getResponseProperty(oldOperation); diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json index 289bc899..6d5facba 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json @@ -129,23 +129,23 @@ ] } }, - "/pet/findByStatus": { + "/pet/findByName": { "get": { "tags": [ "pet" ], - "summary": "Finds Pets by status", - "description": "Multiple status values can be provided with comma separated strings", - "operationId": "findPetsByStatus", + "summary": "Finds Pets by name", + "description": "Multiple name values can be provided with comma separated strings", + "operationId": "findPetsByName", "produces": [ "application/xml", "application/json" ], "parameters": [ { - "name": "status", + "name": "name", "in": "query", - "description": "Status values that need to be considered for filter", + "description": "Name values that need to be considered for filter", "required": true, "type": "array", "items": { @@ -158,13 +158,7 @@ "default": "available" }, "collectionFormat": "multi" - }, - { - "name": "id", - "in": "query", - "required": false, - "type": "string" - } + } ], "responses": { "200": { @@ -177,7 +171,7 @@ } }, "400": { - "description": "Invalid status value" + "description": "Invalid name value" } }, "security": [ @@ -190,12 +184,13 @@ ] } }, - "/pet/findBySomething": { + + "/pet/findByStatus": { "get": { "tags": [ "pet" ], - "summary": "Finds Pets by something", + "summary": "Finds Pets by status", "description": "Multiple status values can be provided with comma separated strings", "operationId": "findPetsByStatus", "produces": [ @@ -219,13 +214,7 @@ "default": "available" }, "collectionFormat": "multi" - }, - { - "name": "id", - "in": "query", - "required": false, - "type": "string" - } + } ], "responses": { "200": { @@ -274,6 +263,17 @@ "type": "string" }, "collectionFormat": "multi" + }, + { + "name": "tags2", + "in": "query", + "description": "Tags2 to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" } ], "responses": { @@ -949,9 +949,6 @@ "firstName": { "type": "string" }, - "nickname": { - "type": "string" - }, "lastName": { "type": "string" }, @@ -964,6 +961,9 @@ "phone": { "type": "string" }, + "favorite": { + "$ref": "#/definitions/Pet" + }, "userStatus": { "type": "integer", "format": "int32", @@ -1017,6 +1017,12 @@ "type": "string" } }, + "owner": { + "$ref": "#/definitions/User" + }, + "parent": { + "$ref": "#/definitions/Pet" + }, "tags": { "type": "array", "xml": { @@ -1061,4 +1067,4 @@ "description": "Find out more about Swagger", "url": "http://swagger.io" } -} \ No newline at end of file +} From c983b5437b935a9968534896dbb8c8fb816035a2 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Mon, 29 Jun 2020 14:16:09 +0200 Subject: [PATCH 07/18] Fix style css for backward icon --- .../swagger/diff/output/HtmlRender.java | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index 834c5f69..e3f63563 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -24,14 +24,19 @@ public class HtmlRender implements Render { private static final String NON_BACKWARDS_CHANGES = "Non-backwards changes"; + private final String title; + private final List cssLinks; + private final List scriptsJsLinks; - private boolean showBackwardsIncompatibilities; + + private boolean showBackwardsIncompatibilities; public HtmlRender() { this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); } + public HtmlRender(final String title, final String linkCss) { this.title = title; cssLinks = new ArrayList(); @@ -69,14 +74,14 @@ public String render(final SwaggerDiff diff) { public String renderHtml(ContainerTag ol_new, ContainerTag ol_miss, ContainerTag ol_changed, ContainerTag p_versions) { List cssLinksTags = new ArrayList(); - if(!cssLinks.isEmpty()) { + if (!cssLinks.isEmpty()) { for (String cssLink : cssLinks) { cssLinksTags.add(link().withRel("stylesheet").withHref(cssLink)); } } List cssScriptsJsTags = new ArrayList(); - if(!scriptsJsLinks.isEmpty()){ + if (!scriptsJsLinks.isEmpty()) { for (String scriptJs : scriptsJsLinks) { cssScriptsJsTags.add(script().withType("text/javascript").withSrc(scriptJs)); } @@ -84,11 +89,11 @@ public String renderHtml(ContainerTag ol_new, ContainerTag ol_miss, ContainerTag // Build articles List articles = new ArrayList(); - if(showBackwardsIncompatibilities) { - articles.add(span().with(i_backwardsIncompatibilitiesWarning(),span().withText(" " + NON_BACKWARDS_CHANGES )).withStyle("float:right")); + if (showBackwardsIncompatibilities) { + articles.add(span().with(i_backwardsIncompatibilitiesWarning(), span().withText(" " + NON_BACKWARDS_CHANGES)).withStyle("float:right")); articles.add(br()); } - articles.add( div_headArticle("What's New", "new", ol_new)); + articles.add(div_headArticle("What's New", "new", ol_new)); articles.add(div_headArticle("What's Missed", "missed", ol_miss)); articles.add(div_headArticle("What's Changed", "changed", ol_changed)); @@ -164,32 +169,32 @@ private ContainerTag li_missingEndpoint(final String method, final String path, del().withText(path)).with(i_backwardsIncompatibilitiesWarning()).with(span(null == desc ? "" : " " + desc)); } - private ContainerTag ol_changed(final List changedEndpoints){ + private ContainerTag ol_changed(final List changedEndpoints) { if (null == changedEndpoints) { return ol().withId("changed"); } ContainerTag ol = ol().withId("changed"); - for (ChangedEndpoint changedEndpoint:changedEndpoints){ + for (ChangedEndpoint changedEndpoint : changedEndpoints) { String pathUrl = changedEndpoint.getPathUrl(); Map changedOperations = changedEndpoint.getChangedOperations(); - for (Entry entry : changedOperations.entrySet()){ + for (Entry entry : changedOperations.entrySet()) { String method = entry.getKey().toString(); ChangedOperation changedOperation = entry.getValue(); String desc = changedOperation.getSummary(); ContainerTag ul_detail = ul().withClass("detail"); - if (changedOperation.isDiffParam()){ + if (changedOperation.isDiffParam()) { ul_detail.with(li().with(h3("Parameter")).with(ul_param(changedOperation))); } - if (changedOperation.isDiffProp()){ + if (changedOperation.isDiffProp()) { ul_detail.with(li().with(h3("Return Type")).with(ul_response(changedOperation))); } ContainerTag li = li(); li.with(span(method).withClass(method)).withText(pathUrl); - if(!changedEndpoint.isBackwardsCompatible()) { + if (!changedEndpoint.isBackwardsCompatible()) { li.with(i_backwardsIncompatibilitiesWarning()); } - li.with(span(null == desc ? "" : " " + desc)).with(ul_detail); + li.with(span(null == desc ? "" : " " + desc)).with(ul_detail); ol.with(li); } } @@ -199,15 +204,15 @@ private ContainerTag ol_changed(final List changedEndpoints){ private ContainerTag ul_response(final ChangedOperation changedOperation) { List addProps = changedOperation.getAddProps(); List delProps = changedOperation.getMissingProps(); - ListchangProps = changedOperation.getChangedProps(); + List changProps = changedOperation.getChangedProps(); ContainerTag ul = ul().withClass("change response"); - for (ElProperty prop : addProps){ + for (ElProperty prop : addProps) { ul.with(li_addProp(prop)); } - for (ElProperty prop : changProps){ + for (ElProperty prop : changProps) { ul.with(li_changeTypeProp(prop)); } - for (ElProperty prop : delProps){ + for (ElProperty prop : delProps) { ul.with(li_missingProp(prop)); } return ul; @@ -221,7 +226,7 @@ private ContainerTag li_missingProp(final ElProperty prop) { private ContainerTag li_addProp(final ElProperty prop) { Property property = prop.getProperty(); ContainerTag li = li().withText("Add ").with(textField(prop.getEl())); - if(prop.getProperty()!= null && prop.getProperty().getRequired()) { + if (prop.getProperty() != null && prop.getProperty().getRequired()) { li.withText(" required").with(i_backwardsIncompatibilitiesWarning()); } return li.with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); @@ -246,47 +251,47 @@ private ContainerTag ul_param(final ChangedOperation changedOperation) { List delParameters = changedOperation.getMissingParameters(); List changedParameters = changedOperation.getChangedParameters(); ContainerTag ul = ul().withClass("change param"); - for (Parameter param : addParameters){ + for (Parameter param : addParameters) { ul.with(li_addParam(param)); } - for (ChangedParameter param : changedParameters){ + for (ChangedParameter param : changedParameters) { List increased = param.getIncreased(); - for (ElProperty prop : increased){ + for (ElProperty prop : increased) { ul.with(li_addProp(prop)); } } - for (ChangedParameter param : changedParameters){ + for (ChangedParameter param : changedParameters) { List requiredChanges = param.getRequiredChanges(); - for (ElProperty prop : requiredChanges){ + for (ElProperty prop : requiredChanges) { ul.with(li_changeRequiredProp(prop)); } } - for (ChangedParameter param : changedParameters){ + for (ChangedParameter param : changedParameters) { List typesChanges = param.getTypesChanges(); - for (ElProperty prop : typesChanges){ + for (ElProperty prop : typesChanges) { ul.with(li_changeTypeProp(prop)); } } - for (ChangedParameter param : changedParameters){ + for (ChangedParameter param : changedParameters) { if (param.isChangeRequired() || param.isChangeDescription() || param.isChangeType()) { ul.with(li_changedParam(param)); } } - for (ChangedParameter param : changedParameters){ + for (ChangedParameter param : changedParameters) { List missing = param.getMissing(); - for (ElProperty prop : missing){ + for (ElProperty prop : missing) { ul.with(li_missingProp(prop)); } } - for (Parameter param : delParameters){ + for (Parameter param : delParameters) { ul.with(li_missingParam(param)); } return ul; } - private ContainerTag li_addParam(final Parameter param){ + private ContainerTag li_addParam(final Parameter param) { ContainerTag li = li(); - if(param.getRequired()) { + if (param.getRequired()) { li.withText("Add required ").with(textField(param.getName())).with(i_backwardsIncompatibilitiesWarning()); } else { li.withText("Add ").with(textField(param.getName())); @@ -294,30 +299,30 @@ private ContainerTag li_addParam(final Parameter param){ return li.with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); } - private ContainerTag li_missingParam(final Parameter param){ + private ContainerTag li_missingParam(final Parameter param) { return li().withClass("missing").with(span("Delete")).with(del(textField(param.getName()))).with(i_backwardsIncompatibilitiesWarning()).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); } - private ContainerTag li_changedParam(final ChangedParameter changeParam){ + private ContainerTag li_changedParam(final ChangedParameter changeParam) { boolean changeRequired = changeParam.isChangeRequired(); boolean changeDescription = changeParam.isChangeDescription(); boolean changeType = changeParam.isChangeType(); Parameter rightParam = changeParam.getRightParameter(); Parameter leftParam = changeParam.getLeftParameter(); ContainerTag li = li().with(textField(rightParam.getName())); - if(changeRequired || changeType || changeDescription) { + if (changeRequired || changeType || changeDescription) { li.withText(" : "); - if (changeRequired){ - li.with(br()).withText(" - becomes " + (rightParam.getRequired() ? "required " : "not required ")); + if (changeRequired) { + li.with(br()).withText(" - becomes " + (rightParam.getRequired() ? "required" : "not required")); } if (changeType) { - li.with(br()).withText(" - changes type "); + li.with(br()).withText(" - changes type"); } - if (changeDescription){ + if (changeDescription) { li.with(br()).withText(" - notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); } } - if(!changeParam.isBackwardsCompatible()) { + if (!changeParam.isBackwardsCompatible()) { li.with(i_backwardsIncompatibilitiesWarning()); } return li; @@ -325,7 +330,8 @@ private ContainerTag li_changedParam(final ChangedParameter changeParam){ /** Add icon if modifications make backwards incompatibilies. */ private ContainerTag i_backwardsIncompatibilitiesWarning() { - return showBackwardsIncompatibilities ? i().withClass("fas fa-exclamation-circle warnbackward").withTitle(NON_BACKWARDS_CHANGES):null; + return showBackwardsIncompatibilities ? i().withStyle("margin-left:0.5em;") + .withClass("fas fa-exclamation-circle warnbackward").withTitle(NON_BACKWARDS_CHANGES) : null; } } From 43fffb956486d092b0508a0b364e44faa3008ce5 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Mon, 29 Jun 2020 14:52:51 +0200 Subject: [PATCH 08/18] Update Readme --- README.md | 16 ++++++++++++++-- changelog.png | Bin 80175 -> 320732 bytes 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fb9a686..31769ac8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ![Build Status](https://travis-ci.org/Sayi/swagger-diff.svg?branch=master) ![jdk1.8+](https://img.shields.io/badge/jdk-1.8%2B-orange.svg) [![Coverage Status](https://coveralls.io/repos/github/Sayi/swagger-diff/badge.svg)](https://coveralls.io/github/Sayi/swagger-diff) [![Maven](https://maven-badges.herokuapp.com/maven-central/com.deepoove/swagger-diff/badge.svg?style=plastic)](https://maven-badges.herokuapp.com/maven-central/com.deepoove/swagger-diff) Compare two swagger API specifications(1.x or v2.0) and render the difference to html file or markdown file. +You can also see the non-backwards compatible changes. ## :black_large_square: Command line interface (CLI) @@ -73,11 +74,22 @@ v2.0 SwaggerDiff.compareV2("petstore_v2_1.json", "petstore_v2_2.json"); ``` +## Check Backward comptibility +```java +SwaggerDiff diff = SwaggerDiff.compareV2("petstore_v2_1.json", "petstore_v2_2.json"); +boolean isBackwardCompatible = diff.isBackwardsCompatible(); +``` + ## Render difference #### HTML ```java -String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") +List css = new ArrayList<>(); +css.add("https://use.fontawesome.com/releases/v5.0.6/css/all.css"); +css.add("http://deepoove.com/swagger-diff/stylesheets/demo.css"); +List scripts = new ArrayList(); + +String html = new HtmlRender("Changelog", css, scripts) + .withBackwardsIncompatibilities() .render(diff); try { diff --git a/changelog.png b/changelog.png index b24a2e181fce68df7b5deb71dd04425e976610a2..9e8b47af28a3ac07737405be7aa36d5d62c18f44 100644 GIT binary patch literal 320732 zcmbSz2{=@5`#(vFqOz4;DrK7_J2RqE2^C{sLiWAEScVA|S=%gS8I;HxvM-Ztl^A7T zN5t3%V;P2--yt{qUId-Dd}lcGJEI%Di{{(RIsfdquoF4C%Wi(hR;r4_(yPSLq50bu?wGbTkyp zJe6G*b|6OfbjoziVl}}ZhQmCUqMUwS4&Osl&0KRWHhPNgx`6mI;R_E68`IN^F0;@c znxJ8HV;DjNsy051j;6i7XC2*~;7D_0Ic`wbgu+9~w>-smdN+;0(Sx@8*n@V3CeXgu z*nQSVkf#1jW@(bqDIE*R`b&3?2-HhK<w4>oujXY|qH@O?29WRoJ+LWvDRjNN5n=&`f((G3zo8q{(7Jbej`-{W3-PEKKZ>a_>b2-QAlFpEGYqDmzGAceB`}VYP${Ld3W%VWC2=)(Rwz{PjwX2Qf&Rh)V?1cXXFD0{bP%p}Uoh1Wc9CA-iSI(`E7ZbP>I*boh6+S2`|NWCu2 z8HdNmPjFb8hDNgaJp9VV{pGd6mq^~Xk40H^7y8eC;kY8CCcR;BsYL16c}I}w_5DA> zW5P~U8HneJu?b(ap9-EmJm0yHpke}ga_0$y_k}QbNxJgA7cIWO7$}f=e9kZd~mowu5fAnMaMq&GrdMVi2%Teit;fn%$ z+|D1lMSH@@@YdOn^iN9W9=H%Sqz=;(RhaMG-CJfaO(%7;Q|Kn~+U|y7?a&JzaEvX3!bM@+5=X*MR?y^hE;iIKC>@+@9aI&z3h9; zA6l+FIlLxY5JAiP2%?(c-qn3{l=)FRH%kMZpyKVu=VeFT>Z()tnfdlB`>BcRbC-(1 zyU?FtXM*z`@`_)U>0JMO@ao=%$^PdR5*0(iVp(AUru>>YhQZJ=<|xE8iUdu%|2CT3 zb=WwR_q~=yQ-M^EMGo!Tw^jy1UVMsaAA%hxQhg5`@YcBA^pwHpIAy-6dtDcL^Sjyf z&=~oK;vU9}?Tk?F2t`8~Q;AQ9I1AoWRQB#=czK@Z@-F*Z$8w5}7ZY6k+ z+YufHbP#O#SQ(>B?Q;9R~| zdFg4}K09Uwdhtt~DH?58-*F8_cD-!+3Y&nwqw{~TVoB3p8y_z{{-8iof+Hl|syCh| zAu`5--9TC|M7kEaezq&n(+cl?to6NN8MjH@4ezrO?=yyr78-~O2w}76(vi3YKW`TD za~0uuR+D|?-Fy~2f+1&Ou3ya*a1!P@o_MeF&C~e@mye%)7UtYX#Rb}exC_UgKQXxy_)6|DO82tJiQE&jCwYZTk9j^BdhGd8?>5Vw-SSCdC*mH7 zeysec{weNb^e2Bd|1jkS)M52QH(%?--YmKC;uPnpq-WqP+Rn#Zmo?MIZ{luH1}~G)*3VQ_FTGl7w)$iarW}@B&8O~~W>#Q!e~7l4$#-y_ z&7Z+P4{VMs35X#JZceXP@7W*xc#r!Y`aR(XypASHdRtyT>v3kkEE|`^nE?my5A(Ei zRy}9S_S0H{t++>zzibb+`(alv^;ud(w$G}=ro+m~ZpJ#%QmLivGcnYifz?XkjL#Ru z_|5Tvaq008K@K%JwF_z-2i|bsvHxiNH1(w5$zxB?KCM2fuL+JXyjG`4xE7Zn6F($b z*JARe@r9;zzn#~&9KX!3ZFf_HQmxfiSvCiGq~Vgrm^wF_Gq%v8H1i7_iZP3#pr0J9mkkn()eS{+OASg5S9|69 zj`u(7nJF%}(kQm>x9QI-bS{`JC@SQ(l5NlHnuGg=DfgQ>p&QB1HjNq7Lv%y%yj(oV zaxdf-dBS-ed0iF1CIuzEmV+vqx}Q`iN+Kl6%9(qdH?1{3T zXzp&6ZHHImJxdNLCRfM%_eBqLPRTam-(vlc{1jEnCKinbS&4$#?bZrjO1l z`gTop*%^6QE>%pBBqbyl<6lco#DBHYiz?@J)F@ZO5kz9=sG?Mr)K#fGO}E4|&L5sXv|%L(^F4nh`w%jza#B!A z8m2$49>9ZsflX*=HbQj@4KsG_P2Q&lCbQqhAAyDwU$9#X18=_{N@>kNr9ie!oS%1T>L1={avf7RZq4S!x^w>~-&I>K)| zEVWz8$i@hhC`%X;Yt0cadfaPRWIW!}YIr6C=JI18iI7QH9N*L%F)K{_vAof-rpYA6 z@x^q0xbh?3LBmsHEWI1eLJVkEMWo)g=s$+J=*a0W!5W(+4hQ9*qLdlDCC;!2Au~5p zkylopl%#_-WF>?oo=Q}fD0#27vhNWM(^oQ(x3S(7>B=gE=Id)4`M>^#WnUK z>T~?(#$qqWxZKS$t*SW(s9opK^7u4oF0%4`+4paak0L!2#AISz*WVfzt~JJf6kqOS z?qoN9XdGSw?u#v)?TDV?cPvS824TteAs*^Yt@o00rE8?6E5E?Sb1yo^6q{B`d6^He zmShyP=DIYSjSnsQ-$LENwxiA>D@q`~$Y$D=zzWnX(hT{?{cR548SMYJ za-D@=YA2JWN4i-vqM~6Mi2X?E~H@_ zUa#DkRR__VD+I{TEavNb>$jRlR`&&nuk}SvAMI9j+3TuQ{v1gd!(T+N4!-b9aI2h& zA}57aj>EGlZe%Oh5bdVNeio~(SnVq0bl`ApQAk(_zapnn8YIixX}N6tVbjw((?h15 zs3>IL+O?IO{K_TmU`RIv?n@?wKjOssH7}p|&a=7Zg*=Z$M}TnI(YuZ~*?Gy(AZKarChwsc zJ*EVS=pI<>2(p`dC@nly+ftE;Qd6L`vQd}fa(&5I1XxfddWA1Z5Yq?=CIkU8cf z?dDR=$B51>x`(_2T}enusZ~fwswKpxq9TJCOZNr_PJd)gwfD&oS=SauiQL74?MreUf00G zK=Yb{m8-La#cfwhYYA^>H|ld}l)M#yLuYFb3juFuCl{E4xAL){S116-)YG720zWVD za8y2Kpm|+D&GnA8fV9Lpi8IH*i~<4zN_TGCDBQeoY3p|2FXdx)9v*HAAP^i5mw-!2 zxZbe^Ny^L1gU*}e$CU`+x|ZzxxluT1uPIm{RJc`aR&7J*}$zz)UyiL?Y*s?3@+F^12hB1 z0H2kUl~ek8!@qv|&yc@vg~F`wsJS`=cY1*TL-nnj|NF;(-uQD&!~cvaDJ_5YzsLNq zpSEsQ0#VogU$po|=$~f+N`o1dK)-hl%y_%K@;CZS!~SeBkne(`m)0hy(D_k6(7UV!NZtC&O}cgD__Fe^2J0Q(n(g(Reuw!w&LF z3GHl`1UL<@FO+AF6s-pCWOMJ*1lEYQ4V+O(I}(KPhlhWO*x}vdjjvC!G5&8$HCvER zFteDsw_y0)Hgad<0@%=|VuP&+0?gah^-Ji^?hh=bqOwq^))usTr`Rw^0kFY%KPU|2 z?;U+@XA@T$WO{eI7H4^*0at-MMDv#{xW!2m&AVC-yc7yc9f6q-9f$1aYlOq|-!1IU0l*-ryI?9b9%O1v>IoU}|~`^16sTtnB;G`*uY0CAz@auU2UWY=}7qW9zV; z4LzlwQZAWI91tOzpk43Au>PIA-QH}5T|N!CwcHy=6fF=1{9n4+b~f{anQDPmBQOAH z3Io0xCwE42k@w!x;*x_ed}R6JugNy;U_uA>};pj(^X(~6JpCI6Xvxz_7HJS?kF?y7eyT_3fVG0=0REs zTo}!y!_k}5>E4UQ`V|8aqS12TSpKuGoCjd}+7Dk!D3;C+oZsB%RMVxfFqpJ*8Nw3Y zK_+&*BBHRSrI)P^9NP)~4bBEC5qUNRh)Mz)F9RHkqU&oCoH*(j(5wb|B^&YtTm!^B zK++{(iGQKLiF~NRQpCZuHvm4PZ`F2});USXrZu!0DV_RrMm$AhB?T0ctoe`o5aNd- z_Ycx>VvF8%JHir&c}jWeX>|D+uMfni$>9d1OZ1S>hB`cSP`2;*o&!8Rv$#1zw$5nr zb62l2?fG)LD}(U8;O69ZOf7rF-IXF*N)s7(jG)4nRklxNSSEb-==c8YM76cd&C za+5mA9kAoANv3nMG8f-*khJD~>f0>bJIgT8drw$3BuS)}>HJW?WS>IyXS-2;_X=G2 zjuA~{jh$0>u&?~=KMgEH!7-{qt5f3ry!0s?A|PWrYRWsl&L73%oyq@OY@7oe@U~D6p4Z#P#Z8zZ7!KpZL4J9q=rAXrBU5|UEHt%y~X$o&v zi(Bu9iS9tOuE)LytcHe~8w);^Rad?yr*db8RDNuL&H(Wv7MSprrnEh%rU;YM;2N@D zT6)3hOVLM;O4(qdY%{bTW|LQt3qKt5#Xhl#%xTv4EpxxH%!TSj`WWt;vo9J7i_54! zgt5j-FXujIxAGhL;h&v*$T+|KFrKgTh$u)_$t-EXMrmORJ^F-UE>gA-Kli5OTvtrj zVxV+)l&9WNcFo>^mLv#>&u6Wn4Bz`{;XOecm#k5doPHF+ch>FtOx1UitBCw|$c{z610cZ$&Bu(f<+f?=C?_d^H>KlOMtF9?JGtoqJ; z&?}=S(|%bCjqmL?4+ojT?5ku5Em}=tGap*Ht3Zv;3ttVa*VxnEz73qsQ=Z?DS{QQ# zId^jxm~mKwrKH4MOSRW+5%^f_J5T50R$uoGJ;fh_b0w{X!84|z5Y1#ZY|Z4j2n9zTLpx0q zSEA#RqcNtQH$sfNn{Uq8&cVGeQcSg#lSATlaFl+ZeB#HxX{ZT)=0|$YXZ&|g)L`!0 z`I2epgThYLRuLaX*g#r-KgKvmiuBET#&U%K4QtEqb|K<(P-l3bP3v=B^DiGTPtDf4 z(}r$J=9LCxP(zl}a>QVMvsdsugym@j^2->m$=J|yeD0ktbe#b;peSQXfK+TIrQ>^X z`WX^l6X>P=sGUITB0HcRY=h%`=6$lo9+5W1;`?j-2@ga^wY4E%Yy2K!6q&$Cs}R!> zD{*em=`|hfi=%JWXVY{q54k>V8SRO7>d(cM@(&CS6IfiT1(`1Jb2#R_yGl|QU)qQg z=8I%S`pv2~J(GC-mS|h_miXk|#eLw6s{i?dBtIQaiHha^Of9H|O_PM)Pa2fIY!bg$&Pv zW1d4md)W5Wr_j=6nNezUyPNQQeq~d|H*K?~{4VQt!Y!Ovs>%(Qhsq07Ikb4sx!vka z9XtTeFB$n<(3*7sii#1TXK7Zgzb~S%bKM(zF>}g~IBwivk>LO8Gwaag)c`5nmxpW> zyaS&qDj2~mn7L}30bJG)1e9I1c|p(9Z+*s4GT$@coa{O1ftA-jGcpU$F1HTaSFY5ZgD~b?q=g>OmzpA7Ykgm2 zh3<>`y)0${V_cmh1EH!drz1#%fO4lKgT+yeTGMFRnv%^{-VYhu(jrOAvt4?KT^Jvr z(m74F)`}0 z?rP}fUv4;JmhQWJf@Of)b_k{(ayqa3i~<>+A52hoL@xBpnz6$*lRIiEM>YoNAiQrJ zyo$X5!PXENy`fBgTum73Y*rVC91AGy58$0+MdZECjQ)F1eL_4Ch%Z#7OrOmsZq$hl zkW%5*m6l1E8nO|O*Ft?*O|pVLKf;AFpnIOkP&#;!D#b|}3v0O$4!;_Bdl;EbU})_N z3c3`db@TmlpEeSFX+N*tlmls_x#;OABp}okE;MCSSG1>#bOa; zPMY?%`J6oqYVcv;J?fZS<#3TQX!vNw*f6EUNMrgcd%QZcabDdR3FW82A_OnVV=wZ{ z`*iJdKOk0(bF0k|NL=;j5L^=+yk>yFjXNqV#VJH>w8^;7Gk|p!S|B-gHOL({SXpJ~g za5l6^i!NX-15%%k+=MmstOzR~MQA~@S;{BATt46J;Ji6wE$5G|6OmA+8X=kHl&rLT zkH!097b?{`UOQtevJNLT!exnw+FJDnF45eZ3($63Oqg;jIK92in=A1{uPmRZ>V9se zvXLQ>xu3lfpvBq!nG`1{hhdE2=AQKnyjc;Hm9NNpqL>>N$}M##y^PrQQgOc`9t3YH zUmfgBMG_ySx;t~+Z!n#SwfJUCxUjijdIxlw@LB9# z#pn4k(L86rsVnTb^ru)}LJV_YJMOjCLv2$JJ^RZUC}nm;fOBJlJK%OS&iKF-H}lvI zlQ-SXv&+bf?_@{bGqq+;h$am@$#rzA6qsewF~`((rb{OIdkGU0RX@D3^dhYkV@*oh zL>kf;uss%WM1$r_>FpbYMfBzgt;*@uEz%L(0l$OfQf1hn{~D{(<2eGdY)tHB-%LtM ztXNGguxNK+kJEYNvnsZKsD^j>yYonjrl_cShNSsgOTG{kHIj@epuy6?Wsk_R*cBO{NB7>?#06d z8NACwR(RjbIzm+WOqKUcOq}9UDSqPNFrg}kYZ;7wanSX4YENEYZc{*U8soxGFJ@+F z#W}mUI~`BkJhR~B*;^0Fq3dp=d_JBL1Gh{!(jD}e%h7cXj|L9s3%TDd`@O) z^3ykdi7-%F8X+vKWrMONre|IW2VpXXJdI5jN-5Jbq9W`4`Wadllge4T2lF@vX0_tW z6l-xsD~d{8n(3dK`=oTtBJtS8gu>)j7Fn*MONnn=qS)!k%Kk<(Nb=-1Y0^;&GMAz= z_aCFocTJDRHu0JDoKrB#1~fSqrX8+td(D-*a%!G^Buoh0?`5VSxACM%NM7P9E5eNJ zgw@}N69b3X>|C6}?8GHV!vwh3fOunG>G=jD^JMZR9&hqdc7J8hZ(w3#)-ilCJ;Jv2 zdG%%Uh2~A>a9xSYDrXe5&$&fpF1FO6wgYb+IM^?|S%fgiXwu+91bmO|9(}}wnORz! z1#PUvgPI-%2KQBDU|z6SCT)h75Wbx!xxb+x1K_3u>qMb+L8i?aEkGK2>3Ek4_ji=q zG$!LV@W&v98Nry&jYiWuTGx^nKOe)KK2QaC@j@-RRhwwzyzu^$)kQ2+b<49}E8ZMZ zKW%sdU!A49n0G|i3-E*=e1G)!+R@SxEdM%iv0A8$oryYx@7jjT5L=gwAn zJQ6vO5mp!eE%qD{(XA8RRO(vcd!t5#A4d3U9+n!sr!*1;2FzmZnInjr%_4JGZXE@= zi6HYvjrP~ETAb^>Ny9Lby!cXWxOYJ%UQc@uis8VKgOi(%*erTnf3!Uk4ES&(gD<&6 zEAqc&`DuU5Ld5mK;eA3S0dS4Ua}>lzOx);1b89xhQvgqY@G#jyh&%Sd`=GQ2|CA8| zAAay$&D-TBg&ZH0c@kHC3!+Gk^2AFlav0YBk$v=uTJ)QEhpTa}a6tG{6-an3?g9Iy zdg~BgeaYFmylepcY<${+i^)z!xR%JIfVK9E0k?t0V2eLz%B{ZF)%wB_bPl?}pqV<+A>m>TatZb@nbWn-ZsDL zZ5k-a1h6C?2Q`h&GU=PMA(0Dfi+N_sbDxG`FYFk>88jX~ih$%AeSSJW7VhbTu(m0> zs+d4o8}xLa9*urCNJ`MQh3MCbAoBtcBu)2GA*3sJ(c55J!{XWLLR8ClwFi3pr*j=< z*v&`o{XHrK=1Ww}7JLGx3-gJIEia8s&B zUD(@`m0ewCmYOeSM^sUTZg&VTHyy+NOz*}*X0U6u77Ve<>ZkX`er{u}VDu>D4WJZ` z-wy>rX3ChSr^JYe71~kDBq+BCpX|br;%FKP4_wjmG?^JpS=VX0G#dJtUB`vrAg`?F z)0JYH{l<7wTWSkGSUlM52*s{!d4|;gRJ^UDcpHBx^#^8Kq_w z{52c8GzgwVQKPy#U-f5S6`?+z)0;1U5fA?R^hrwT&$NsGs}Y#d4^7nCO{%qPS$$f! z5?h|pkS#Uz#?n~wmZVnfrFnVH=@m>sb=^!&z@W7|w&^sl{tSYYQ{)L{GRiP!t{RT;_UYq>qB`4?lz9>0fb3Z-d?ds` zvW3lv{KF6uT{_Zbt11W*%wgOGV}-f$;Zo4 zDEjPXY{;Ty5N3=k>hBX0fkPY)V6a}@GqjmK;{ z?>c9Ol|4O5N6yoO(6u z)Z#RlTY^3ci3WePR}~&YXbvp=uwsX4Ot0tjdOhTQA)7Pdid@_d1Cr)8s)|%LTB5;h z!h>E`oqk!Kb3KJOcxu-4sDiLw9!a(;7KD-jvL)912sd1w9_)vrwV)`p=O}a`BLkJG z5Zxh<_}C_gyX($7lr3wEi82)?SJnCR8xSBP!j-ewR0y;gO#EPe$#ErVY_{5}D|Df9 zuClES#)ZboQ@DYEoMAw=)!%d1U6uTO@6O3wysEtNW|;7klleSuhd7j6`R)n=b5i`< zJ0+dFjFZw{Ih3I`KYA7P8aXzRaY=lD8#;Byi%QTonfn3#rS}4+_+2Bj`FsV;aWaNI zYob<=Dn(sBYf)qIRFlGs>jN*K(<1sU zv#C)h%komuBiN+-t>~Eh7|!BlKpfD2aFv_7cSOe9EyVs>)oN>wFF~1iy&cmyN#g1I zu6%_KP3m_XWzkmZ`<~q4ZmnnP8}q#xUKX2WgIsu_F@kN+xCl5ai>QPpi4s>JGB-D$ zV|nQw;oKZumfd9P0I-_4hjl zU{?`pH<#nT@FIK^4+3L*$EuKV2V|o_u9Z5SlBeHNzC6<>NJ~c=NE&f9U0_@;HP@(E zqoyG0B$5r5<=?;v#a$1d7K*ec6)#CipMhm}ksoa)Mt)ZPivJ{XKCaZXb=~L7$6a5! z{@#IBxn;liM=eh5N^gb;IF6%+um>NW@3i_b>pbRLd>)vjI=@EE{GfXX!+Ecb-2I0( z))tnhLD2x8pUmT1EjO?)|IXf>4edAWtHrI_obSletZeCa~>8_%QQIwu#RfYv6{sM^QKLVa`jumwPM!*r|zCqpE`*q zPZR)}A@!Fuv-Kk=#G}GZiE9Lr z^~4>-v3L7wP*c%J4{DP5xpT>Q-$JnI%BO6i1hcC7UT}sF9uVoH6XK<&uMVy{N)1I= zNkAZGNs`Lui}oZQF7Dgxxy}8bO0hiMw$S|M{%?S7fFGiofjzk;#_v3>V@ji2x|TmD zv{Ss6>q-jyZIjQj-F#WN+gEnaRg_3}|J` zF@UM|I>0>%Kpw9h7Dbt0F6btK-(HRy!;%)4WJjdXVA-3*^6c0Tgh^8_AVih-V4S@Y zLunp#2Q2(CE6nGrZcX4JeI*&mx?bZua0Uh1DyjK%RsLL}iP8GJpxS7>c+=0|^Bxu+ zgQ>YiLQ8Ma=v{(#^ih}%{D|MEtybvR9v1s)q{Y;<`^Ss6B}s74`In}v;VoT8mCgo| zihbYT@n3Y5bBkjysiKI1Rww(?<&b}Nj!vnv2nqK5X~?$X7Bn8J0u*{&Q)UI znAGEtH13Uw?|d2mT|0TvVPmZKllv%yG9)}+CinF6bf1!Ow|aBevPVfCDH-^_+~hf1 zy}&R^%q__$W;OFwfwa)1m9UzN^1T2*_S{}BUcBYP7?V+6^&F}aK#uH&^*2#A?ovGc zfJ;K}lDiYyM;?mJBbL=zZetgGL)5WAdR%C8m+G6A1R#It)u??n1N`^K9PO@Z32JJp zd8lM1dKe}h$#zH6*&-%3doaVbmoc%bpIk8cf^tLhVZJ%sXJB|)yE%+>^JwA_*x)PNN$ariH9CcPJkZn z0uuIAPar?j6rAms9B{10y)I_VbO**Nus)Htfu3hsCz!yiS$86p5de2LQD&<26aBEJ zhWE!m4Vn%96ipFkxg#~QHr4D@y?YV$H2IJm5YCajGjT`qdtU!YZaJ)Cxvf~=J`~2x z)BQd9sY@lb0w|SvQ{nG}XTWSuw4L?6#%63xkMd(G<3Lre8BNz;shmpy&5ljF)>DRj z95+s9(+ohli#I|yYCQFvC(bP8_|1ie^Ou#Xp`%W4mM!TtHBKB7vIu-=#a9&ZOW)pL z_&83{qcoz$Z34&8Xrjpg8SvwptyuJR16{bvEuAVFD}i7(ge^P2?9OCt7zRJ!;R!S` zi~<1L5kbY@0`+b{mEF*TymXvC6F4p()JdDHh%_6LzCE0>sKFbBvxbI7m(L7POUD#U z+P_NenhnOxqF$`r0(7&aM_Wo(?b71H>g||n^v>a>aWSRb!s7ymt~gL12_Az=?=odX z+-jkOqGn2vaibMnX8@bleQ6BW(A+*W;gC74A>j5Ywdq3EkE&O*-N`?a6U&OI99Tm!9wf8mmJdw3Im!xDeOwYptM2DNd+w&Wk4AdEEhdROzcrpOjxtLbsR-eZu)Gnt zaOY*ZXVr?x0ZEte+%&+jxWsHM@qn3+=f0OY7{v4%lc*@n2ZYvhJ`ngv((QHnBi*k4 zeTwLy!TQhIulUVF2($GSX;Ne`19OC@y0i`LMBu{GGkq%{$1PpiWn^FgC<)7!7`;zH zldg?0Lr>e(C8HjwB6WKgM_;`uA{om? z4O?(Jy7|Xm*Z~^>5LY>{_Xj&F9Kh&%J)@m9a|HHL3w=tnh$fYaPS!izX%&?Q99Pc; zjlbta>HycLf7UV|{#nc1wQiZagOUg8NP*I$%cLxy3HL2&yHDjTV*=Kr~B)nBV_dXoF(?*qNRR(%oxi9{xG0K3wH*x{OE zfcSX1!G3nsv_rpm?2ZOjp&Yie=V5KI%pQhO`eceu{Jq&vUA6hHmY0{*EmY3Wm)DdG zI_zxTOCYNB4ed(%ABPry*>s_x82UdB9eb%3M=rLq(by3)rZCfU=prNlh6?d>{j@WN zT94co`Kt-MQa5%6P0v$+CCzqU6`(Zp>d=!NQkt8ZN|5QNmJAnt!0aU8@{>C2$toJ7 z@uzH%yHTW}q4+~P(v!5iT!D({bJprWpVZ!+EqeF*TWaqN8zVhCiZsvnd}k96tl<2q zpwq%Io|I!WI}>9--;wTt_uNz z(y#$HgWlMUa=fi3zMT=fu9+gN~Q9yr^Qm*jfEegfvs+?XjS-)n1qcxoHxy1-ZZ_3_JDU0dH(Q&1YM5u3o;t!?FGXgBiEL@Mk8b}@X3AP7j^NDOhU4Pz5{~IZ~bv^-% z5L=5lHm_uJrxr@9qb8~$J}xQlLz~aRlhdrBli`*2T3h=Y*uY9HU8c+m6RW%f=C)gl zmaYV{nQH}(-;BYrd1)aTSdJV~E5r!);)Y9TmV)MCGD`X60b$PDH-E$A9|u5hqFCml z$x+|rg!^XW?^-eBlE#4LM?%f=^F0G=< zwt?eoxeZDaiK>_)90tfNVwxei+o;p~lSIyg_Cjo@4+hLRCfI|2Q-#CM#qKj6y5Li= zZ}9RqM2&Z)J77*XCaSgs7Y!30iOnmCF)6X|pOZVuxBk3F+M?*s2Dn)2`^TJ#N2P!w z5JSUxA1#i1$qCe~n(z6T3HsE@6B~@B+W^?RL<=;Gyq-ZX6jJiXffhDx)Netp0^tM# z5e&E46B(*aXMOBAe9gd2${>ihTcU4U25F5^6ZJ<1yncGVHyjgn6ozYz5Zh29&1PW; zLwW0oSr?cZbbaN2%Om|?DHnNB2B2fvytJ)q8?`aJ-h6|tVBwNys8i@nW_aa*3AVZb z%)w&*5wVvxM1N@b&n>@g7<#R;r2%i)Tw;9keUxnriW_ExYpUwXnSwDY+rF{=iVLWz zeZ3`M4|*E|wqu4<#BdQjXlqH=xq%ubB>^Tdi+5!o;Sm4`Z$1RyVIiQyihmYv=PcGC z@@K|yWkSprGFkQ|_EO3Jp}l{myzwrIXTyC-=am_6 zA$k;mfnEZx+H`>`H#Njx7yieBP(gE2*|GOXEiM2Z!?7);Lvy-dU7v$6!B}Q+{uyt# zh$Bkp^gfG#DQJJ@OWP6@5M9go)VJI30{F*lGEWD(n!4Y()*=eVM9JbLfCNVvhM?1c zd-qL@D7J2*W9%aN3;ildjDUYsL@5CZnA8pzrI-_J<7J>9RNC^Y@(9mP!-@!kh*tmZ)-3$vQdb&cLdzQ z+Ujjz5-<%Fn1I#VeeAErr7kGf;uH{4V$c7vtZj|fQSu&%tHt&D%&)chzyMX~SDCw2 zDwS!q6`%#&`zHL|^esMHZO=TPm-Bpxtg%h;=QF&e5(R_Rj&}P$FIw zWS>nmIf{_D;Nr0lZqw&WC`A~*cF6fYIommEx%6f zAq>J47bWOzEwZEf?l5c+y^Cg>1^y|@O&I~ZYplfh+}2D&b}@;7IiZNWy6V!~zs613 zLl4m0O-KCUKNqzP;fb4o`K57;ZhH`E!RkVN4cGRwr}|N zI|Wis?gnUV$t}Ju54TCX%>xG~UIWvWt=+#p+a;af@wnCYO)2N?<=zJ_qYq*I6J%1f zfPtoOzZ8G^Ycn(b0%L(rXfeP`TLtafB3gW9|I@@QglxGMH!W9s`qm-^*Vt2RnFS=ZRvCn_&d`7 z9pfJtgJ1i;aR7Bp z4Fb5X^v@?{#V3VlD?$fcjnVp7m|=|MO9S4gd)LtMAT1n_ALgu1kFpOhcb> z^6v!3lm_smnw9=P#?%7ofhP^^-r7PHf2u|R)LeIyi}8sqy+;79kVZFfZ2JShp}&2g zdk6qRP+Iu5C<&&zmX?yh%R*{few#I9U9!XH0YEy}lwdpcPZHAZ8ax~?53@-`EPirtJZf7X4yx%HtF~&2ip)oDP8YOmDSFCx z-Mz?Jw%lbr`aOg{Dpf6{QL>4rXhL=+@G*SAU0sPd9K3c7OzOonrkoF6f1eY($#&K6 zh0Wr95W=EY+isujd^egbJy%6&-8hX}@5rzmi_tgxAY4(M4~T=+&Lm0D_B1h!+b>$e zC0j3_+dBQe5TLA?%v?>(Z;7>iaBGtB+KY3iSBCuX-V>=I6`KvhiEgSJ6Y7wug-6qy zt5|;FB#)b5lu)$NS+4>2rYuQAthEi`hE zzNce}uy21YzVZ~j_&B=Wv)i=$WsJFN+4l!M)^Vnb^3t-hIOwBn=s_*P?`&e4;?S!@ zZgmF;qvZ8yGS3shh$eR}ul&c3RE~gc(;Z2*h@vZP zTK^4?aFKZok}M_D?^F~oY1NoE^lh>%2|grG1u4*;PsujQ80A=2^_iC+gtf}!>JO>& zeEsfKi(@|CoTD~hu24M;^W?1dHwPFHkAHl0TQvEqrlbR@K4Bi^?T}TQFu)q5dYSlt zzePy}w6|()JfQ&is53%VbxGt^G(UE|tJL?0JCFd6jEr1ZYS(VM(=4vPfB;$*qpW>_ z4wxkK`SObJp&AhIqM_B*kNbZq0@_{5f?Ij^id8iAhCAVTHMqIAg@f9R{Q~FS%X%$*_I^}`>0;`p$1?V_Fcnl3luR&;4bPW1)z0WRyuyGl~`U)P0cm;sY)tB*Knw47XJygf;wi!dH@uQj>^l?4uZXF1YA># zES3j8=&AqZxs)(vZqTb=`~fp>-&i-8@I}h!$Ywb%IsAO^o`%n&`8@QX}|^?}vN)9tX7QB%HIqnNvAeQI2s%lKKZEH#Z`96=h0#Z4wOJ z8`vuo@4$z?E`_sC;(X?ZYn{>09*+lfLiHQWf!SZCMD@5g@e&;E^D7iUVObE`{v`i@i;vV_lE!ryym4$Q-^0+8D36>tmjO~2(xmHZR?m<#Et=5+&omZbRcNthkD)qeB)ix$he@b@HqrGZcAH5Fc zx${xb{flIiZmw5SG8#}%G1!_5O@I_QeW4NqCjI#SWsxDnzkDQ2)H1iYO9bztsAVze ziWuc~2QXc_Tny;}q-FnyhS~h|N6Uc>eDQc|l4CjnyEzB!mVrOwsg}ZNr1@*jgf6dHK+r^wMkz`_v>h++Nso=nuUhrX|om2D2K z)^I||3(@`SS-h{@?l8K=^$nE6tUf*xT$|)X6C0`y`}%6F1Mi2}80E*PP0W(aea2#l zK&>fWZZtxw&pb;mALu6e&Vr1`LmS?E2_uPh{EGtam2-UZvvX#pKv3-Nyf9?#(?%TA zXW?fNIw@6b$3f`WC{7&jpksdZBC2<3=3Vp~5&!iQcaanlmOWa#cABkTV%D*t0cgxw zL$4I)k6HDZizdNKx+mjkS9+|Xvmo4`zHbmPnBbLk+%vF_3MuntR2X0glmRWqQumj2 z-d2Ln0RrnS)A+WBFyT+lfvrdfVX9qcxwq}JYQTFMn69t+x0i71=-yW;E{ybEoqY_( z06MTrPW_$!r-v(W$$nnaPO06^|3}(a$3>OC@rr^0q8NxGBDR2n(g+MF7OlhpL#ZG` zNJw`n*q|t-z@XBtLr8-KNDN3fNDSSb_c{CHjPAPY=X3A>9Z3af0Azuu%QoL1dPN*9IEq6R#2cj(m}kY)g%s z-QVk#r36(bPnyAoT$d*ZB+e@V_mrM18J5+2OlI%RTbgdNoeec=Yr)Jqb$Bv;eSMF$ zQ6Oh$uiWbvWoIE3S9y1GwFBpDwveM1tCM^|r>>8f0IOZQyHjYl_}&=Z5bN3p${in^ zr@Fs5Uy>QQV37$S4oB-mwJNLIaQyt@KHKJJd&xUo7WVhG?(~&+R5z-bKBE#P&zL9E z_S}W)K#mW4yViVzv~zKUv*+-1o{(>=&sLDTXKO1JFmrmZUbkBsZg4^!_r?=2SNQex z)zamS819p|+a%HQ_yurQ+MlNkeh+MJ9(}sj_W4n{{_zE+3mxOMIU>U!bi+?ssh(76 zHrXFM)E_#H@V`$)sk@Wgxld*;d8ho+7jBnl$Yhnvd|#CAP-K3L{`95-m>f&?q9BiS zcDtM}yEHzVq_^zazffP;nA;DbH7Rb$|BPb2Jp^$kbo!C+qCy>vS_3=+V=`~k(yerJ zyFM@~AS;)pWv{iNJrPA)(p^wf{QOuU5*lCj`=GQjr5dV~1PFZW&2#vMzwR}Atl;!x z$6Q~|OzW$A;#E>-fD8vk)DyU(T-0OYvw^h_YHkYo*~U16y7zW;Pqle&PO=*}_F!W0M}PRln?|UskyJ5IE~^ z+$YDzehga!8gg>tS!tEX%jJFG)XlmEj(fg--sG=g+91$&#QsvrOvYpj`|(f?IGK9q zl{UyY8gn`4w(dR^HnjMKNvG6x?)9YUPf!0_3)0#Q#{ z+@XLoiedH;5R@q>8B9Kv+)`Yp!5S^kGy-weY{fmyP9My?0g@%!-?wT^F`RQ|U(5l@ z_Y@k+v5VpwUuE+yT%tW4Fl~F)gTy}SoKLmkg{c{@@qRBcNH2fz5+IG^QfbJAIQtMy zM2W^+Xc|~j>5d5L0`9y;i^r^DnTxgBBHF$=T=4s*NH-T+;iTO8ev^rlD|2!+nMyUo ziW{#ca7M<2_ZQYY}8)6Px$3*nEM3`I0iV|ER7ot#DA~3aK?Dze#!6m1QMF_p#y&U9LS_uHN1J+Sq2triiNtyO%FWXM`X2%T(rg zEyeY4`vuQ8+f^SZKiqsk`u5&!Z-R?g7DI6Zi8T519cPFtNZ|S8srAa?;bVLJR z)nA=zTqmF<<~Yq)6K{PfZk_SQnppjTEwi|~jd1AEhud#AVFrZg#}aX=-_9ISB@DMu zK>U9}G7(!P@xHaGrCK8Aq7UoInKswFO{N3(%$apYm5uS&GO^!>T63}t{Mr~fv4Og{ z1FCIICd?i+v7d(h1KNq+`*`(b^N*b1rB}-Xi!yzh{$bp!G?MsA^E7dTo~ck>^**Os z=?><@wUL(J2^klkFb6G}FDpJxwb?*Ob$Ex}W}7DWN}yPn9ITtkx0tBoRe`Ort={8Q zOE96=4B*yj)ohx-5GOtmuq{Q{)x~9b4nHyRdTHMBO&<3L$$p0uYNP6$msArq8^VKz#Kr`Ri}|2KHayDKz$ZM|aV)L&B|R_%ie39GvEnK?I&qs!=u{ZM1-QMK5a#%z0H%#Cc@5$(z=;c3C=>dV_> zu>~g;!|w~-Avr9UaOVX_aKvjj(GH&ZRM?m#$GOW`r7rm8RB5dc{xZy{>Ifdt83>%R zpX|NLu9cb6P}Bd8;4&Z6;4+Yq-u31Y7f;i$bVY5(yrCy=3nPJC9>S@rW$m{owm^q| z4LL&i4hgqUHSUK|m0Pq|UFr?HxuP0bLLVBY<+`E}94kUa-WoAO-d?}=W z3<1aamwbl4-L1U%@)yfX3$I-8yR|qKG#Ce4bo$M;537wfsJ~M$#Hr+lko%@zndKVB zUKmKj)^KCv15IB>&Que&;j8#-zYXG~nwQFj3ojVIYPU@3%HXDORNcL6JZNRS^!9;S z7Im9pFI0{1UV`eWon{6HdgU()E@4HJvONx~S7uTr5$ZK-(G+~PusO7omFdK6yl$|& z9Q`Y!R2_PMBjUNwxR?(h#gW`uYm&G0R=&MwkgwJ>+>#~tp$X(GSuf9Jsr2>?H+EE5 ztykq!JhM?*j7dvo&E1TI+YO$238%}YYapNZ1;5(F;;=YYI7Hh*)GzbRbzZTdy zcocul!(wowk7Be3F*xKh# zyKC92q7(ukSguNBF_{_&ST|X+zP~GejoCh*+G7P|gJMe^L`e}%WqZH2xSR7O_k!Y> z@cZFr`oFXn{@|CIYUoq=B^4c)G;*_}8!HOW$l;S)(=0-mE)*v2kR?YDi;PUO`1Pc4jR*_(tk7}dXT`96XWQNBh*7A zqazu8Q)v^@_PZ`#<-4N3y8xeXez9=B>)p(R7M*uL2rKJ|HP4hTV+#!fiBQPv*@>@L zUH5gX*yPi`6)2@0g1o=2G;M*2^Tkl&tP>ooUj!`0dyxp@I$T+UJToy3!yy3{oS{%+Ak-oQO z-{-l|X2_!7P<34^+cTM3+rS*<_c_5R%YGBa`2LjFUei+nl_qg7ldZJ|l-Bk3zb?}n zYhZp$C<)*Wka~W!_|1h!!7Y(D(w4Jv;_Ik3Ok{w*a$O$H2pcgt>p_~w^pEH^<}QxQ z#6XyLMQnhM9_MM__dP#s5wDWG#lb!A%#;ixvw=-I6p|lcdFvi_J80pn z+M2YTr&MCJb25n{MorX&w--rIwxK5YKJ8%w_U!hDhk>5w(OW)mH&8Hk&a4k+a?vRG_Fo{DYwn(teNvSjR4QZ0vC3h?+f!ciP$>RiDM9h)&L%L^k%pJMDruQ~R9^Wz+nZnnYqcoEOpj6jK7dQa^KQy z!stNlh{vo|v{QdcyW^r&VhkJt2}$YY2x-4k^BK~U1!nsO;DLhJ)C&XiR-^I-oO9*; z;aQfG_QQ`*`*$A8CLHtT^4HrQq-QtDrl`Y{u%q6HxmV~HckmZ`NV!KX$Mk#InSz`(3xBRox&WsH`JjfTzc+ zCg5s9o1n6xBl0wcQ4a*(h-=@8`vbvATcqXX1sMGj@D1;8KB`XAX&W@Ez_$5ZGp)6o zi4vLBe6J_9<9u4|I1%?<~Lx19> zbY|`DB~gdTXai5?)kpnkLu8>jv@cc)=I5s{2|{k5l>pf##as$npI2zDJ;Cz$;D+Sp|g{nFhT5F)JmYP2R* zfq)tR@re87GSn3d1_!)(W%tSem&RJ{I-gAQw%L!d@5XK<>53vO}`XP~5U?u#bp%kN` zL?d#bV9_MSGP)rb3MpL=j)k6Tdc?%-pQx3kzvp?I_hO_{0xjwRgW{2ju%%Oj?AAC0}4UkR?u_7eFzp9bbJ z#&&1asw|<2aI4Z*S|ggv zt`U{L@QbTM`F-DzDj_hgAQ!?2ktT9AZGPEH{^zRN_Fob&GWXxLpXiYS`y{!qT*(!b zDyQC)N&7e`C^2bZGiJZA#pk;ksXTFMm2WS-3bW0y=vu~bRN;fDB6 zpROD%lpa-=B-X^KrV|Vy$F}Ldk-(E<-f`}oN7tdPX3U1Pu8}T-4Yu35@)ocY$aJS|vxB~c*Zt}{fg-yrfoH;ONEw> zK>WPG@8$%hg9q4#n#h@z-nTyC7Tw?UbzCbQbHV=V>qs{36}Xk`6A>OBSP{*i1kIQt zOOhCVe_d?Re6B&9YEr0yN=KXPO3U3A@#R_Fs-&~316q|=j6QkM%jGTmwzU(NH}_@f zs>jykEl+XpIGP73^Wf8r^1@i5p-C60R?4|y3HGz29Y>oJ1TH+<#Xs=IeFQv06RB_W zou&|)`l1mW)8Sdo3fmMm2nM`OJ%9Mgt(#+hyaL7I3vp)$h=z<-OG{_d`SlVz^yB(O ztcNqmjRUcHb$udj;Zi#$9Ph1fvzAL8PhTH1RdNtp#jS(5@Sd4wh;JM2zL}ys$ZRKW zGZ-j3*Y(^cx^Zb)qwfA!Br=M&R6xBV+xS{*4rQK|eRm&{o~4t$QDY$SnvTJV%Sw^! zK5Dcn610(xL2@l(!uU*^>+ep|&Z{wd*F22&tg+bPv-791 znb_-8YpEKqwv9o)7K7JFzUF>HE3-P$Zr_R7L6)Q@W*3{LtT@LkQt^n3OKm2w%DLPl z8*!flIU#=S)@=>dTCSLsm&Eyj0J@ZOsl>jl$m^~Hbd2S?i$z&n+j@`4r}O zj8bysb!mF0t$TRR%Zp7_Uz3#8Nxo)C3Ki=fE8HId{zog+zGsip5O#xc1rcI5^~4B9 zLaNai^e~7lRb+#P_YpCs&Cyp6EqnmMPZ3XRc-& zDIa3g6VX)QLEz4r^wo5ps&NqN9fQ8!6)W#@iCC|B>vASdb9aN|%g~e*AR}4L(KW_4 zJg&Fb6D~d8ePG8?+2k$V{YsMakh-c!Axx2}x+R?tYtoy`Uwga6)S$u``x1|wXAMxE-NuJTAf!hZX%ZA$8zai@%{+BCp1 z-6WqPCcHMFC+E%nc%<&#N4kilb0a5l>}^IkYiDR&MJi^h$hDl&cTO{?{HW}?WqQKk z$%9Ey)yt18#ZduZJF_WBo_)d?D^(l3s0>xe2^8scM%D^Ob^ zjpVJE#`0*?JhQZ96dM6X^ZWVlE{>4a3mg=_vZf}q!xaQA)B+USl8acqx|9ZW$h)u@%kE8MNTDeZmuY zceXou51%o&MTr}B1!5m2+Cxldf6+6PlJ|E|C8UVhkLxMzYfulWU7BUiui^NHvsb(0 z!UqpvtEJhpA9DitOgKy6_K)*>Fis!#AhAf6g_RB2ZVple?j$ZtN%X5gmA8ytqYDM;s_C*o6AO(nw znQbEC6@)wP?trh2|K-^aa#p09=_pYxC1wrtsDwga70lY?K%D{~TY_Ait#P-BxS$8g zMccW@d)2$51)(v zDh1LUs);@7`7eIwuNAcOxnxQCjc#WqMubXXZLbLkx5oeGv1ZFoCSlCr9o`FP;3 zb6UGB6Dn4}gx4smAFnCEYg3~FkiUQJ(b*H!gr#?lrQ28U66J=+PRNu}qmkafJdN!> z_zt$?RtHd)vF?N=RhDwR9(plWC?+Hxc^BR9tTf;=ll)&5QFV#5C;)h*09t^nfUvP)~r1UE25*k!gXV{RP5nGA3Osr%HrpuGGS3kNv9?#x<|R= z=c2A5i;AhZhf>5&fo(2@*7s>3(AiO@8(lWzk z`Pk#Ww}ab>;{LDHQN5}|^vc;YX#aod6)c2$2iq)*tS1ItWxgX4rTKR5*#kh#^pw(# zt7}+14vX}OD-ZsIFu1kzj-Y#EYv1aAMhX$A?-YS@IsngLDk(kuhiBv;foo_!a-mCZ zzv?c|iP<0ZgLtVfz;IQ}+(=+T7j6{WTbL`6_K{L2QrRG}X1(k-+VUFm&pAh}&LIN-@wysATG!ZgcAKq}UHpgoQgwiVpvYUWoioZ2U^`E^P7;cydUeJGYjR z&}p?~cMBCZz>&3EuGt`hzVcs|*S-TNfl)VzMAx3ngfv1hF2N5;x$sp%bY*56K_S1M z;rX{97Nt+oD_~@{{b^?Ohx@9CV>s_k2kWw?vNuK7mCp{lj42TK1Mz1sfHl*>zO+Y` zt&Ilc>&4x>)Zpw~w*`u?-fJfb*9q1>Q>Q5F@0*oq1#u>qJ*q_iNg^99HA3uAN7I#o zGLk9^%uLk!CiKF8BN!79%thU>2PK#b6U8{R=}`ChtlrZK+4-1IKeJW13FCOf71xWNhNOQl6cPbVj>q-12CS63?o zDW8RhA8rdeb^Vu}QFerGBn>uVmc&h@*xqKKPs_o>hErNnjn#_bWNCUk%F^8iHi9b$3f5o!R*|DwBpd~XkUOT685P{B)1Mu&a2{#R4D~k|Yt;p-=oP*{=A3fcvO@OlyAc$Bc-LK z9~!Xnhc?~Wia8F|+yF68Bz2${Y`!)!a1o$odQ0^tI<7O}W(XqiL>q*f7;!Q)1x>>3CKE}2Frz~@ZW!wW71&!r&nW8(C-`&?@lWXE&Je7!l zgvF;dip?}1g4#-|ja*Td+PfRmxv8%UM!?kIvc{o}ShVR_@U;<o7E>DXxE|>zSk-2z@d%XNQ)U8AY8(Z<5m-PLu4xiI0QI1?U&{keOG0mu zse+su0_zUx1Yp2^hVrcfW`A1u$6d3FL{e9$4~8eeq(1r?AB+trn{DH*J2cC}D^qZX z!jtPvr&I{MuEMO-BDdF3%z=9{otkTWmBNyjhJ!<9j7L19PwE*nMMcGW!hvAH`ggkb z4j((#ly1&hz@x!Gqo;@tDqF*U{6bpFJd<&XpHs zMcwXGW2&SL1Rqv04$$&6F{A3}10uG=DKq=++Nt)4Ii%#6nLK6@R{ZL--)!hxu&`w~ z^l7G-3+`-)00g2wp`BRf8%J-wkKaf+zkX?9Mg}@&>*ll5c5uwbPXvqFzu&xv=Wy4H zO_+P(9Td;__YLjz$cOS!S461?BW`DeWR(2T*=(!g?d*WU&aE)>^q~kA6TQ*Wu#mA<^m|Ltz$fvaRus` zL(WO2dQ8xxF@gf8O<=1Dc*v6Qi(S7vF1i3hG=+Wf~`~ zbnbl4EH}FP`W%fglzbwfM{BVR2iW_S=6&$7D&d-qCs{zXwitn;PQHUaTCH^B3e4k_ zWxY3P68Nk48mCIy-AlTVZrT!#OEo$>G6#xO0*Gq;T!z={s^!}>Y^zcMjYa6I?e7>8RLWka%okSmdm#m1cl^2*EtPf6|8g3B@leU6MCh%!BcVA6$Uy@0v^c9^R>D?kBue?^ptJw0YXT*e;3h5&Nry=FfE2W+!*ROEL>dX+|0B9NVo`SW}fA=$tuj8(LFF5@80RY zZo{{ZPxpoj=@{3NWJc#8ko+E}%B`MZ5#ihtEWjt4$QTtO*Y@mNke^n3Bx$~sm!y`} zqm*|%j4>rdF7A~M%mkKm%|SeRUa}Uuu-ZXLwwe16q1=rG^9Cz2Piylw?l~m$Ntfc9 zHg+jVSe~8-vdvLO}^pP_VKFLUl&Wts^O=)udwFv_(z9S|fE70=|jG9P& zIf`H@EWN!PLa3UWwGrF&rsjQ!+^1CL%mkG<|4og{5FqwobX?{lvZ3uiU7@xSV5lzO z{w)gk+18UH_7vzG+_=i%sAFt$qq1h*!3H<-x*K?#4MI(0vJq)@i1p zAf9FvBJ1J}`<<>q=Li~nB>=`)JD-?4Z&W!T(&RznOpJeP#L5| z#D9_U2BiMH1>}0W9rm;+al=LtkVG!KDur2Z4z}NGCt{UyVV288CoU6#z;^*?M{o%u z#vKW>gYoB@vTV(Q1kFMhA$C>-v$wDaI1a5$+Y!@|EWlyG$;D$e?l?KsxH*3=Eu1%t z@Qsg&%waYH^aZtmGHJMJHZTFrrIOY<`Wzpufhj~x1{IA)%>DaL-?%H3wBNV@KcGIb z5JS}leY_1q#{7fu9p=kmt39-_aa$09A1ACV7Aki_Th*ZAc1J%l>bS1<{2h`>W6Cq6 zYh_!SIRc_?R$J3pM`~VAm=enZ2wQN}!&cRG-{hI-L*nXy*hBA7)wh3Y*!Kst(pNu7_QbM?rE3`lX|Zf`Vc1GgmA9*00xcx{!VH(FQ}aCaPod# z_v{j-okDvd{~HV{a_yAoqImD~I{EYKLS6Lr^zLJC$o-ljhWCo=Q4u60{mWhnrYBjz zoJBE=2FlslgRhQUfq7#8CbK*(dGKK(vT-UFc?-iL>Ul0Xe1_jYV{~Tr@*C+POqg~r zrO)~L{$@$Ic8){2X{GslY<^3wb5@Huw45_Ky!H+{8+U?7@{g=WM{n!1SFjw@wFQH;xJ)y;cP)HK9qW%v!oBLL+OvDB&}f}Eb|^&X_8TOp`N*H^W=6*=+kr;A0upzW=jlc%eJ@77kLhlA8aZHfb(lZk;#u2 zfKm|%BQ?&!rot5?q-zuBtW=$I`yyX;#YBa^FYFbr4OrvNDrV%dIDNM8E3_cwqMQ1^ zbMNkDqEDfrv(f0BrAY1HgZN%rr@*(X-}*t z&!mn^b>4*h<;$m((l9w^Tze>``B^!rt+Zi-=g>+To(AksZGW0dtn$0MWQgeB-}pki zqZUDm_SH$Jrlh_%C+k=*eQNgOT{(xrYQVFxNr_-|)JEQ5Tp?z=!0D%PQm}vw@Da(Q zvVqq^Zk(H84y)y-uL02pbk_n|e1#nWR;F?S9MSK9q*wjIe!XQMRzQ;REFh3Z?|mYT zg~59PO0fCEFn^uqA@1DSH+$ab@a%@(o~6)#Rr(}8gB`gP9K4s3C0*T7F$kX=iypJo zr5GD?G^!wCler-BrH^v&LJ;iiQNcH643lTUweS__;u)-V|2 zS}XHL!f9Qvlv@ovVd7Yub2Nmhxs-#p@Kv65tTpP+!cpzS+)n1BOn4uWZW7uq1y|QPqmjij)J#1~I)x7Hy(PMa&%!8bYe#u$*AaB>BR*#WVPFmOj8X zI4euWGGndNz`1>cQH3Ec_}1EW1N>+srnlbQOEFdBPHMTMu-E!qUuC}FoqHiEC~_Rh zVH#b`tcp=OHkY@uAl_G-@M;cEOWb^4@sbTB&|Gy%Iy%t7HWd$13DOj?pvAZXvcRI% zp4>q0_rf|R!h$6Tj+Z2tn5ENRVk>Kn)#cijc*})8H8SFlAkMBBvh`cuhWvPELcj_2 z8$PKXE5qEZy)4Fn&+yUx54Xi|-wVp*e?#t^S*z^i?JZ(UL2`XO*dEYs37|H;?r(l+ zud&!#^W-!bKmC?zJy1H0z%16(4C*cOT+3f04B*vLf_R&iVFWHgZQ#~$J5gI9ZfY01 z;%DI#CDSkw%>i?jK`jXKxN3`!XJIA);^;Bjjs~;0@iRcwQb)zkH@D-V5x%D{X!vK@5+SGKDS->S$`_HTqua)UBB2AFF zpC^b#^+1%s43+bnlokSU>DAj3S23gdp*zT7yw}Sk!ck`!Y>* z8f1}bUR(E@0+8q?Odm2Tiod*ZaN=)cIB`1c&KUU9n8$UP$%jaSG<;#BdjNpOM`Ux|5xP4 zaNi$Mp}&s>ykx&DE4vf0%htbOm+Pq)0i)R-xnAfFKm9A4-iiVU1EL!@@CfqVZG^B7 zuUgN6Nz%^2;nGd_nRC-e4(uH`L-w5VF$McF?d6n<+QPf# z4AYh~_5k72gAuz1Og~ia!XadD@Ma&pGdyDinb>z2zdDhQ)Xw5HGrkX-HKxS8;Lx3p{|qpz6+!T z39rXtPOKOLAS9OHaR$f9t2;<_>u0X<_rI<}X_DoLo#{_-_gM*~y7YDp=_DHJL2Xd2 z0Gp)hqzyu8)%X3#@eWAJ!lnH5Hj_} z&U$)Z@Tw0~5*qjTmXA!BQ_5ifFd$cULvMjj@|iJ(7GsB?a_&<5@EFCizGobj8CCxj zKn}K)-$h6Ng7u8m@i3jZ^@89Vk=PA}D8h|uVj*^`{+7w4W%aB@5J_J_dvf+0>2`ed zL~_Z6QOFLtd_>3Z=j1`nBSs@VF(+v+bFJ9YWYxO)V3EvFCLO1D25L18Fk6#&<+Vqz z&NL2gapA4;N?5X6P~J*UV@z|)c8x=AygIZL+V0D-rvn->j8)8`2wYi{ z!BRQb%CDO9Mt(vEEoQ%%1D{N<+NWeFNNxR)XGw^^-C3GqhmIUWMDqqR=!(Umf&#@~k2 ziny*gQ+yS6ZcUVs`&0_OIXVY=TQ!{xeU6c@$FIq?^}!(l2kH;Z|1^z%`^)`4Ei{0R z3kv=$5&<+pxL+oJ9YtC^4}sSu8XY-&Snq7rseU+^;#ou)TpgoyAx>_Cu~MY$GpMm{5ZPH#2jUFiciX|-Rjl>Ul3GJO zgj^MDjy5FQCX@7HT^}+1tq59H6i!B&cKIMU4wedEX;h7i5m5uIDKhtd6S8rtPf$mD z8DV6X@1{BYVcl4*;VKp#m(^pryf|k8I=5l6PaZl;dH`=%pYQv7n*5UtZvb2%%-r9+ z19hhNy(KPQT+c{&D@oF#ScQ*Fqe`P0E=%*E#Qtl}(?429Dqg)+A=ltLghNdwki-%n z)8j`oF8Q#&Q3(wx)5=kg>ovNmlQ7K^3*40-&KyX*a&O2bp?v}Hx{V9)o50y#Kx{WS zmp_4WSrBa{sI#@rJ@`D|1StUOVBcW+7pdb2w~io1P^3D5OcT9>4pZ{x{g~#~tuq(3 zArm)+PK`}PA?+?F7`O?83jU+zn>jU*`W~Bi2?cs#RVx&%5nWw`2fw+lZ%a2l5vH5s zI}ZW*_EVzD)>B~MzIo8lt^NA`&ObaZ`oHI8Ac+0jyr4ZO%rn7X0M+1B;FCfy2zvsT zay;Jxf_sYs8Yb$=K5y*$TH1ng0SL*PtRNg?GkQg7H(Whbg%1y z`W(mf(hw0UUs<}YP znPKTs8OS8wJ`$=^3TLc(87crfKT`s;m+R)OjsXL5T%wJ#97N6qJ>kH>QjXB zux!g~NOHTcuD`R~U+by*x-sT*+cfYJR}(Y)Iq*#XnTJ+G0p`+^(7lvi;x?uV+06yy z`$S7gG!JDHUF$-N5udD(K1m~N^y@HA#}0sPN;<#TCUGX-2`J2n`CbW1QA);)D= zF`y>EEVomoborbiu44iGnLkp)CQmvMoZ{$32GPg(Lzm-^zCq?zo`!8JSE|VGl_mLV2=>u z5jceH^3E&$6Ubf`NM%} zfeiLHZrt$hq;t%NW7F7?Q>X?@a$){9!(su{@xAhqx>HUtrw5~^O%-8dFtv9A;%GXX z`eCI2;Dxr;Nl6vEy8e+P(l3%pl~3AS7UZM@xb{_$n`0nematoHFSw%^DZ7WvkcQfD z)V{Ae0{3Wyc+6sg6ac}SgU}#}PPSA+MF{GqVAHsqa*A}WuFC~JJ%^!ES$W~^ol2hR zV67Enm1B}cOJU;m0MdGZnB=}!41YEbLJwhZUlG;=wHAPJxYwO)pAnt=37V079e8?M zq~7*$*GY~`-Ym}`s*-|i7Iw;^7$v_?>hk5Hk_fTWkdEc?*L;M-$c%NBAx=c_>vKMd zZyPKACIG)|DGve5G(F@+XM#tw))e6L@~BGDX*W@b`Wkp;iqHSo6JXbD0SKCC)0a~y z1=z|V*?fBeJ_24icW!(Z&CA>nyreInq1fBZ{@H-sIUbe+P3cJx;9=J`@3Xs0}w9FKi~h8NNnv0 z#Lz|Kb`Q!Z7bchvXlq+T0V;+<|7tKOLhs-op7K-%o|19*#%`1!4CJ*hA62e^S()(c z8l%`9h@FT>3XY*y{TtW*f$Lne))g4kBS(o@MGv-5wNIoNR*Ak>l>M&~8jMlYGepB& zAK>>n1Xzy}6wZzc6U>7tz4*^TZhnsdhJ&Xo?4*=%UsK16xA*-HEINGTi02u8uK7)v z!Y@2yPydq+w-1KB#xE&Ai@USy!5#6V1>|&;DG( zxxZAH^!V{+sec+AT6?aa7T{lA(Ec|z|3Z4i>UUaq+fne< zY_7;%OD;}}>eIDq5G zM2bi6?LUnMd;69{C)qAi z^YHKha_Eq8!OuQB%`O;(zIKl5CrN()>o$U+EZ_CG5cP!Av}^!8U1Jyg5Y7BPzl1fa zX>1GqlS%$~5}woiQ4@_d81C<*T1{PSSRWjtgr^KeNq>Q6tIa+{{0+qj#-@AbdoX24 zg(5U4uV6W!!(k_8B7$g*jo<&+gvY7fJ!h*ELyI^ z=xo@=^WZft52t1@Q5c~QigSH^&-gFz|L2+*;GAHBuBntA8_F!Ucu@B(oZL*5VC3L2 zAdE$AGX*vLi)5h;MLPDDx8m0+xT7>b4?|0TpX&R6Th0k^M-Wjmz*aiC-=JH8(iUV- zt-&G`nxX%KzMstvPWFLo>IhjBrGAFgPT38IDcS4!%dp71VHxx&%~0z8^0dz|??nz& zr)8x^HS7NUHH?HqA4}y>m2)2jgSaK{wC%r8hH_0k@^X!Vf+`9RI$)g7@`GLmMd28Z z0LQL+kkfv7J>}}v-+nu`3L2WAIWQW&4Nvw$Z~WuY9;=T8s2iA$meh`)$mx%}{s+0# zu4n3@K;qV{ea4i$tQJL>r%(SUN<6$se5dWd_qJE5^%2IOgdcy<{5d*ufU4}{;iE_4 zEEuoayT5=Bz*}ZWI0gS{qU<*Euq{kaE>TSDID1$J`EIEjeAIDbQxT=uNPMq+T&{}6x)bPALaye_16lv^+KCmr~q2G_7~v-(g} zvg;~*55S&U_ou@vLDbFO+D-sfH&lU8F#)P=yaxhj9&I6qUc+WX1BNln_|tPr!}d3T zGP-)ozTDh4K;GeaZClV&x6YabmzZ;%j$`5*6lDnIt1&H)wkUxYdA z6sQtY)=73uK*^}a;q?%^qR{ui22*7{put$^f|)fPN^R;f8kgk(!R z31dmIZ@)bzp8#B1$`~$1WpS<5th<8*Q5OCFm}MmRY)i%Wtdgu<98mQOW@SOASMRX{u_NAN z3W%Wy2TI(ZvLJBrj zXZEw5XB|)Bl8CcDMd4I3Vn?)-zqvi)N~&Mk_DZ&7x%p-QH-Yjp+r3J9B18cQi-`V=YpxkhDV zG?NNt&u_^wD?+r6FE`Qb`FH-oNgVJD%PYHnjOQC|W9}$GxHI%Wb zf6<*~$Q#L6KZ;bE zA&ar;3!<2<-*=UZysIe!{{vl&Brp!6X%&fD?^fi3meia2e_D(tEXJJ4fx<>^Ak5JN z69Jvt!7P>(d$4%)uTt^UUen)3zFZDnr*ymiWw&r}rw55u=UOBsCKICmYpjdvqQ{Zr-JHh0FujN4A`xtvtmiCp#?<+NPX;uzlQh zWyW<(lreN4y4jyvkPL#*wDV^xI+ppbmsshw<2B_>Kj9` zdBS4f0serT3RPklD2yB|`VBgk{&DYW!(iMcz12Zx8V+!SX4Z2!+pY+9UK$k|bzO^o z$ct!rqzl@3LaON%ZdTBh^zeqxQmjF{dJ`O!dUJ7h>}=tM*s2of@!o^lZ_AZfq$T$0 z8#Ac@rX%iZHl0vQmT;IJH1r|$HN${Mu@lvgXO~z{=P@-Ndru(~#ai7)nwnhx5AIRC z7VU{J_|Qa~GsGf+zI2oe-ys(Y)E3O`@sO!+u+T5+lDx_{kTSKil%1L>q#>g z_kqi;w$KxB6p~Ep?d4>m8ud4GS3!a6GRK0qG08Je4tRtYVO`WnX#EW(;zYS_AgXemJzPD7CIV zt^DfB$~Q(;%>%eSf-lQT3dmq2N|)aktTx-PM|hLlCjjZZ_Erx7X;y-bJ;~oAWSR=6 z=vV=znwB#HkAyzm8y(sOuqb-aaG=uJRWUKyq97}qLb~~T-R<{CZYKg``*@{`nIg;Z zffaxXe<^a>NiQb8Qhe{-)RoiAe9k0e_4ZMs*Lr-}%OkiPCC(UsZXHad{yuL&k~+Z= zBW67rPIDH9fMQIu{0l6x!cdW7sH7w%+*EMd{FFFAM06E`g{~rCrA#>U$GQ(%ua1{P zr!8UjoghAjiVAwCf zsYl8)432OXsN-okjXVWpc+ zeCkIy&iP9Zf|Ab<#d0`98{ab60$MK@IN6Ma76At(?Cmq{X14`1Ft2u9mcHBgLYuh& zWFgh`q|}(%VAr~ju!kL@D~l5dsygVNA>iI_!A#)F)~`V*Pblt=0daEMj{TDRDZ_*Z z3!{xGrT%dj8O@x%HDTFpqjao`%-2RB-WV-%+JnT_a9j^`kWYU9a9ei{Ie!hRfzzpg zMAO1eYkc5q7@Po@uz}`Oq^4bmCo&^wc_K>(vp6cbgB2z#lOk?> zdDs&QNK-#RSe(wP3QxX6g7fl-8Ii5ZaX+vC!ERa@C}=Xxl4EroJk2W~ccTslb$W&8 z#di$?Y?ei-3ZNbZx8gcA%yN#vsct!ZM%9=3oTAqYr!Ky{ATsL&K)g8s09>e>M@~#q zk{7-J-&|O^?=_H`0R9FX#5SvWME_zA$8E440^k{omF9Kp9>G~BI8V89jcKw?tvX%` zhH^djyu}H+$)etiN4{`4ukM$n^jh{-TO-UuAI1O7N z*(#&VWRp=scCxvU5@ohfW+i)NudE7XU9vY37cz6%>-T!g$2q6YxqtWlz5lrXIFEC= z`g}gu=e=J0`FsIunU@xGbX1f4n0dimsp9dbI|yQ@_vw*vA;qa{?@11wx-Kdk__<*M zks3oO9_4s@ZYPq##Z4fnTW~MV$8R9iBlslTA2nbA;k47fS3QsE`Qq>5sSAr z-9;O6fGzB(2PFxS*Y>z<$Vq@T z0X6s{)lg;Zpyhx)LUPB(&=}zW`~}?}D&QbwCmOE~?IkLe(!_a>SXJIx-jPQB#znqq}Y!7pVp- zQ;ychK5c32azsU{s0_nJxF$U9{Y~|4+D#I{%LPPe%}66@5X* zNsQiQe@XYhB*_M}BSiYwi7iCDNe5O+#m0HXDesl7OT+CM zS_VB4VI>NcB^&QdfrQpeGT7Bfuuv9Z18XthNF?`}=2ZlhCvy_1&8N}2@Chg&da*EM zc{9@3z*^Z+=h3cRdo;cd51;R>?w*7m3rE3viI3Gk{O!r{XR-Sq11WC(oey)T>W=7x zLANpKX`QdTN?LqEa1$J(tSw30=|d(|W=&HKwpB(go5MpJGNwX8nC@ycRgM_|rBlX_ zoGsrHqgDzfRyoV@TJrXs zy%cxbyvQK%(VE^SY^u{kh=g5>^=lTZHaVm?H%jb828j*ax^zTLeeN%ook z+&K}lhfgLwz6pFb@61(f>8N|^nCqw?Hop{-#HVCERBss{Gl74k4m)$L4qe|8W)*H785||vL8VOF5 z?~jMRca7Iw+-g4v2aw(!177ixf?EHWBedbJ6P3w%qq%C_gSAT;>dDsQIqCD$8@5tE z^5yM+uD(l2_^)OhtL-_8IH2d<9?rL;SZg&IdNLO|A?HOqfXlwv^8vi@)h>8D)u5T~ zCpYd9_}{INdjL~!o`ar&74NgaoU{vq667qez%AiYw&IUYy^o!6@qJxuOlzeHfSy`F z)GJU^jU~tEJ3=Ye4O7&xNBI=k{n6E#49~I`M)M55L&b_V9~?wa%dJ$nB;})-1H--k zF>%2jQV~=?_`WA7*kAN(lMm%hwrlU|w(L|R;g3)^AyHv4p+1U#cV!sLf3)4`duNM- zb{(Hx1fwr1r$309PCu)u_Ohu9a2PE1zbeZn*cCK_jq-Z$3`hY~+#5a2x3o)0cyauL zpt92FneKCEo^1(_C^wT+dfO{ltFig(VYBR4G3T!yYE3+o4lR|4FFfAQKu@M&!P9^& zii!z4eC~%T>P8sdwb;+2*CBe*?%u95CtK++zN=u%dvlg2;4Nw?mMKJCOi1YUbNQG? zgO?&1l;azG^ZJV2aMI$H4V}af+PJXAsbm+KoH~(v z_-lt6J{YIK%S~Q(XfDl9$nNoEEFeBsdFlJzzuKjJ1uAAjcQI7^;2VszvrjJJJlSPL z{TIKg8qwfA76N_Sh$V{xccXA)N264qSK;tKdSZ@i7LfL-Ct-2}zXH#z<0CK<%@-n;-L7 zrusPVc~f$;ia^lt{8a#+6}vSF2s!qJqCAL~p3qOMHNn*pKKS~Aru688Gd3L0$#PzZ zV-jix#;Gd1(uni72U8b+R73-Yg-2{Z;Y66->*gu2)b+>ik=B;&#p^F?RqSDMTG?$d z&ilTF5Dx7yT86F5pD_a3%&fdostl;(lOtgZ3L5*7-EvDa2sX`3wfyk)0fX>}7 zRNvnJ{Sb@N+Ng8MaLyv0##~3_x6!kA#S28o1(?*z zuFQ$~0xNwy>9w-JBR|;x_(mu-7HXQXr}FQ1To7OuG=KR$W4BOX^`MTB##TP(vR}J8 zXY7LxsiBc!Sl6B^HnJAzb*~?OtQTu;OH_aWfB0om&p72!Gb&b zN;xg&M|dK&yH=Wr_ojGcnAzT5Z0QhQY5Z=upuHrZnO#*Ks~5H6)+xCscmC_v+Dk6) zlF|)To3`%7nVFl*!Fe&Er1tiVB)>eusG~v^;(hpmQ7Y>9m)9YsCCFzZdT|nU^|X;D zp^q>!Wg)+N6|lf*Q?+z}m9Qh923+&X3re*&?fkwzx26Gw{34XHAr^|nztpnLXV4NP zgi}OgIL_DPv!X6Mi1IphdsX1o+n3}Ua@9YjrH9wvGEXm?h7L<=4i__N8}9Y8Q(pfp z>~TF`Ss&I%Efzg!GKW11FG!tkr(yjZaU=Rc9LWs+4S&R7QdWpR%aE^8w*(oTdi0C)SjQ>tpDl zEpWR3^GQr=WROAYa7xd=WCk=0KI`2yE0!D)!5}t}l$gjWwz+{cSZ(M;&4cbqu{Mwl zypt@0!@^xbW{yJ>>76}Nkz2JLj&@8Ch3jIqJC3trMRR3YQj)^eH&wfg?lFOoGN zG^BqZ&g5`Ke?`J0t(ES4tGeDj25q*&lZv?uInk+84vPn&9Rimq4niW7{^9XHXml|^jR~(j=uo#`&mr*HLmZ(=6Y(I0pRUx0eDX{oV1Sm& zJJ|?a-hDj(Y?f+g+7f{~u`ShpqZH(w(>sxXYJwvLrCFDBf1P+lN^jOT7@WCiet z2FVI95o@E4qSLKA3}iCNtp=6yatl3f#c)QSOxuF{K%ZYl=ZJ<9?MFAPv0q+@GJ-xA;gQ6%40Vq*jG>Cj0CZk`NJODpFyasO;Y#)jRnEP7)*k4FHp~ zgb~W$P6P5Zq#E?`@a*jC_qUcu<-d1Z;vWAQ_EoiDuF5=$H2;5NtL%lXqWon3=$d80 zrFvw~lWjsgcJx98xcqzgu8?;|t)r9ik=pIn8hm4;ZFxsU$L)KlEu%8e4GZL%zzR1%IO0MwulsOjQkt{sSz_SwNl+;gpp1qJ$agQ7s}wxhMSxb2lE|FO zEGXV`_zgG-Gm#&Lt@ERqCSfH5t1k?8GrSFTIg;N>t{k{&Ce-%W|3i5z}g3f3t#}=sC)__rl0HQ z&P3)?xPN6IjyCSiBqk1I=pSu;-lJ4PD3z0hB|TxDYprgSWM3O7E1Wb`<(CO+cd8k+ zJFNoaP-%?coMo5nG$qb=C>qX=o{hSpSU*5=`!pj9&No`M{Xd*3{&mb znv7J|pMq)G!pA_p9Ne{dyf8GiiF9225l=uTcun6z!BkULI?Hr19AGqtpsx*+a{49f zq&a1nxS8^bj_`@nVzNcb=CZFBg zZrNkaqZ7ftGd`vk>^ww+TdvyesMlrEP9#Jt=DU&#$pu-45t~;WW1C&R3fe}sh2h>c z`}#iy=4+-?V1x{{WV=8k)aWrBfnRUtvT1~HF#$S&9AVAwzRS4$NZ@=N^6AO9qh@&y z&`u#WNq2?XvQmEJv{R=4mQBFV{W!tMI{bt+4Vk~<0nmjkM{=R{STyS9y2P6}bPD_w zV?JoCn(EYa)lx~*M;ijr3mV2o29(>F=JW-qHGD7cea&R0$mlCsokFA>-$@wvJ@!6jE3KVQrqESk z@!R7$(Q`g4nI%`h;gw-y&P^V$oQ5VCZz2P=_<{@Im@tRlE?;r#6c-V?`{SP4C|Huj z7QFKre(t!p1lRduVneuP!P1C%H0f07la08E*^Mir!;U@}XC~TW3R4{02cj*gxn#PpMGn(n* zJ|6G55>lNz>BqQ7NO=5x+U9C8&4p2i>DcT1%0Ys&?VX)r_hlKZ4qVGaG((2S0?6|) zI=lpK-LZjpRJYxzR(+Is_50Kj>O|BiF`+9g?&N_3tZFS#oUhYB;JWenN&Y5{ zd}zAsS)K-XQXDyH(r_f~lwD_fvUj!S)Xf^u!?82WWqD30extzw+V_5s58~KpCIK^_OdOPR$(24* z(Eb)e>J+AMn9CfkBK?)ilW4+ui;xB6@CfCL_$usgYy(-(AY;QepL}-Xh3@9c*huyQL84b!U zHLv+>E4}Xj)eC;rg*wQ)@h+ahAj$jL+CN<00ZXTTa_HXL-!&v~fegpCV;5p<$LEMu zSu#@6&-oK=`~B)9MobMe?h8M4e!Hwcn7XhMB%41{$}%fp;mcdsY7lbybopp}6)lb} zQ54y!QK;)`hxuzsDR_U>y_k`Su@E2%Y8eE`32{^bkVH%lTApgxLEOA3YhP8Je>@F;voaUH^10D*0d>aKzczNZAKJD`dC)rC zH20XvNhWH&rnewD8qeyqIb2R);^XU98^!JdFj~vc4@5Q{@>Em2~e z|2AqHTb(}?7hel~ubPd8?GFl`U$VcL|HCcjM#xs($rY`VQ{-oSY7AM*s4b=Wy6PCy_^hjuMoJVWr{gILgN1HwtdGb@a1&N;~wc?rabfE?s|~3 zGX7ytnutYbK%w~N2{nUI?lx*8?wNP^PA$hBLU6zrK(^XQf~z3kSORPrAsO9@nGe+@p_cHG{tA?i&sTi$uT<*2S` zV_N4URcA-62IE4P4HP%8nmAk*Js(+a&Z17GOVF3vyzoRE*X%p!dQAV02U;;3Tr1WF3rH>U3$!K>Jv@YxDVoI9U&AoU^Auu zTobTbYJYI^`59{hufYpbT;u&RSRelY)$0#?!g{iAx_}vfom-qhn12F$M3U$ z%S#;}+PH(|vdI`^=^5|VrikkB&xc!-9!lHZjjV@8+M~1ZFZ>!~;FC3V=mlz~_R>oW z=#@V+amseyDKXB=LQfIIF2$Omm}PJatUiv88&73^jMf1V@d@~UIaZMEIWT#7ND!hn zF{3ovajFn=P4cQYzup?PZXdj$hmlJ?O^C=Cicr`3JH&ibvwKxpy> z{bgFyIZmqL;ph0q#zx}%9qezAQes3bqQ>~~O6_#*)a-Q$fu_iFJ+(`b-{o0de7GA= z20?}`h2mB3SJfYc1YswMTh4VdJgmD%@Hymf{qUV~PD2VM_R9Y-AMX6|Z=U%BY42`J z)<35(qvGa9&{W5tyFEEt58NApVKWM8k##&>XQ~7`h_U(cSH|mm%MR=hmuoTko?d@- zGQgW7ve9=see}?Z3Ur_TlJGMW9sluAFSYb|N-~t+GBcb#+@S8L0eQp^Rv*bFZhY;l zeRSQqno`5$@qH@>yTeK$j_UM^)EhpQ3%!0ne$9g>K*`;wJWc-LS>K13zXA16or3Kq zBS-=FrERaWNQ7e?S!TJgT}a{x5v;SyxLY&LmzsRlpNkD}ZY;Au)?fQwY=G}>wDw}p z0k8g;BUjZ+GMeS}P@uy@v}6F}S3=D3!=}(Y<`y5gy;mxsLPt9UV(0NUtC@t8-@f18 z;?U;@9wB=?DecIn#4B1YDlVB3r!8qK=_D_oiBpjHqp-DBW85Ao2KkQc*3=KX`6-mL zKqvK;iHQhR=L2EcWE4v~YH?+NZ-$<*S8-pC#`(Dr_`4W=50^`Vdk_80S@LX1+M_|< zYO(LrzI+y94{`1>5!W4Y_Fqxk>15Z|*;G*h4RY$Ft2RJwV+Ec$g_0NZ%$^&L5DTpo zDvS%=ckR2=bju0k91PZlFI#0?$jv}JbYh#wNvR~d?Xm-w-2x|#XHLvg&m7jit$O3J zSS@lJ&NS=X3sP*TF6;LH#d3)Whj8vvg62caP`T(j>nL7K5U;*tjKu_vSRQTt+>?>U zOr(01H9b8|(Qu_V+{*7!M5OaZA4n3}Hl3b4QMY}P?Rl8fjtYj>H%8M|c{03&#a_f_)$urc8zNJu(c1Se$9d$(4D|ajj@WvHJ58gH{GVfhV*8 zpsf^qA4{%I?RzX~s^-ETy>s=?yP&6BQWJ-xobEuftsKEGavFNhGl9MDB9&InXY6^$ znMA?E^7B1BG$Av~?Y_GsmB7E;c4&mNlHFITRu!ZMgU?4jw*;4Y#Jg! zEKfzBEL;x6hKjQ0!oKj?k9Xz=@Wm5(@1cj-izW8=6&8uN=mu{0JUl^pj_5d=bHXuv zXZoJsLbN3AOb~ZdJV(IRNjw0`lNpQ)yaTYsjDW@UYL8<(nDbxpB_%+J{{{(x~TCKD%&F@(g6t^t2y5VCUwbo~Rq#@=sY*^Z(yhC)u>j}-39~s@2g`cS}t~oh3%|IUY2hkN1imN=jC%fCX+-`&w4qJ7rp0Q>& z(^r9dMNWzgiW0X1A~?+ndi=>3syHM66dCejn>%8*I71~GC4m<7r6FV$m@^Z(v>;e9 zZVB9<>~BaXg{PZ!)lR^vH&+gI7#HMw`pdyM!eIw5QryOi{R2lW3H>72<+b`%c35OX zkd#zt=8w9U`fz3O4xv5Orbi6GZ8SE7bcGy5UTw4LG9lSc;Yv^?3D27#w!B5D0@6Fm zY^Z^JF362KZ~#?uDr`1wCJA5gb3q0z(663Bk&r+&aMl1`HfI+xVU}_j z!l2I}`Tvl@UjvET`Y&G|E300+pAH*Zzhj%w9!2g*MAjh)lH3qw=;Ch5J?A*exdicb z;qmH_Q^Q~-ES>Q6?AuCxOC)RGZr*Q?ki@q#Zu<2V$zr16odh#9y|BHh>iVgaREWoB z_tns~uP$@Kj0ed#zF~kjJhfY8JO={BOAu_Ue4h{DXjurKR(5YW>t5{u61;8qvneDP0NJ$-|Wy{KtM?ReA?r)lw!Ej!XXq$jF-g`)d`c!Y|UsbB-1K+`q0VCn2e` zKfJ7`G7lOhp{MI8Zih8#N;k6^jUDuv=`v0_$31sbKlECu)m+x}%^&hqw7=vG*WQ!s zDZBz_m3zwiY^lII+Br?ht_>(A9xl+#S$Nl+<1(kfw~}m2v=7;|%rNe=U-?p(y2aEA zmnyb@v1ef8bwbB%!wnwHQ=-^LD&+V2vlTNIF9C_+xP^WZ*LPtiezCo*UNn;6bApwb z*zWPP1H0y*E?JEx9GSd_lxyD>-K0F&wExlE%*}#xGvZjhg?Zy#vh_r3OUnyBKE5lC zj*dpg#!`uiiLsH9dI^`aq23&O!+(Cz@rD=nd;HKQc?>{4G5<*A(SWrbzcta$)# zT>ZtOf&HIc61|#c-8Cd6B#a;jX=-X3n~}ktk)H0|8Ek8?{yx8aD0)r-hw5qFXw=$> zPMsuEdk$Q=@v{P32%W5J$~i(2j>9wAgtmVd7%)2y@SM@drMs~girM#Id@?)fj<+X1 z!px|HY;6K5H~-N7j)Lhavn1JTr}nqygJ~uSFY64+1ME5)p3d-g(c|JEGpY;uPyC#m z|H0ymqs8a+H*;H?yD+V*4P?^4fsm(<& zdDBj~t_^}i64g9>7R}xDU#IzH7t(T3w~ws-ppNK{Z#j?)Cl~bDBDePHvwkAWJG9=} z*w{q*`uf&GM76cG^@_2vv5}$S9KN{mx3^l~MG+$KdKA*HHLwl76MgZA>h3k!PRf;r zBrD!GDT`L?Li#pK}<>vd^~gpyW`Hr$6n+(+9N?9FiBX2avGIsx+d#0_{)c)TOfS*c*F^< z-@(k0#C&9Ote+thfViR zwIitGd0E<9% zZfH&1uCLbmhl8ikvgya|Tn{|&gFAvahOjeQgRIz6yes){A4Z`d<57%#zKG^&IdeCmZg9~~IMWom; zrzju`AJ&?Lo$Z6-uxqY;Y*^FUzir(kHP2wGWq4V)tlx14-4W4n3R|oKxL|6^hWkII z>I7Wi6TXQ78(;l}brWZ*#s-tdA~bM}zTX+%`|DI@U^zJDQIYD7@3;Nu(Go5qKnvo?W^B^#t7`e zt5Pyf*s%^$`6>ETCUWefKYhY(*aXF6w9f-0u($Z_Js-nq7x+P_p}~G?o|)_`!9sJx zq5a_t%>8Gv)3I;Ix5CDfcu%~HfV?|qmSYs7faUF!ObI{I5 znD!EeC0yp^#q{BCJNlp?EMbHJTgNtR1G8^{ciEeYiDHhs$WExaafk`qdB2{n%fE56BHym}xZK4zSfyigF8&hq4`~S3 zh2y{7zcw@O;K>U7DRg2zE);?^R`BUf*d2;sG9`_^{xuf3KCuV$(RYD#r`wbgAU>5R%Z(@G7 zw>~V*7lAxrCtv-wg9`BDx<mC=5GMI>V$fvjR5# zt)XlBPZ!y6JEc=KFm;Cg(E1CN!)~fLYl3|*o(KyK`)h=ld+O)r*I@xq+^S#I1anA>|1)C;hOCE^>lMYp$mw_ytQH)ot@}Mi!a$$U^9XI zz|hH|r2m|WFm(JgQ#`O?w36`g^FzMZ^y!fE&zoFRz)ljdA#{dfXZM$fxu&3)ud+d> z)7k~P^03obs&gIJ&bD|W+R35nm}`tp`(Mu?9syHqlehlbIEzkVCJycWnf?ImI$haS z`?&Y8+3&}@U%H*+zs@E{E(}s)W-*t4?fD|HtHM}HECCym_*-~I(lCJwm@N729k;*% z7dUz5#Jb3P19vxbbU zXnk6W;HTI>Fv!9kA7x%_!0vDWaW~pgru82?S44-14p;%NwZz&pT{}TDjmnf^(a9oG z^b_Zv>cy=8=r12cWZ=jdzmM66t!mwlnAvus!i_uFBVFM)_Ec7U`Q^s-V^s7B3FSG< zf!)|Rmi7;T_Fxj4QK_)No^l*6GW)UZw7`Fj;fOk^>B_)*tRBY@7Ig0o--v}(uNo| z?AqTR7KNr|p7FE@HvLsU-3CtC{~Her!a3rf*WD|fgRsON8ADmtrp{N~%U# zk$)P=)(7uPVLJi4{;ydZIsrzqu(1lJTDk6lx5Zg`_Y`)E5lG;xn?yCSzkhoKgCuN1 zQJKKv^-cKas!x8DobScjfvORJb5O;3K>R<0XZ4UI$Pube!Y;T;2|7^!#kO9|aN3Df z$AdevCu+WJ3=It(gyQZ?s8lS-vYIy-m+p=^GuZX3uj@Jw-c(arixT$IRY!pI>K~&+ z)~D4c)M9A_GPB4u{B8mOH7O{yo2=ody)SqbQcZ)9g-dWSfIcQyD61256?$KiAYO1T zD9j-O=e9m!EVY7d5U{*b5Luy9bdjaXErR^%(=b4ay^etNE3?ylT{v52;=fJeWfT^l zKbyB5+o)ew!$@XCmTW9)_|ko2gy1X=diuVBtZNQg8ZahYXB@7x+DCx4AR`1CcoNi`-I#I@wO#cZ|>8^#& zndQ36snd{eij;#YyJM^o22%hPlL0W2v&DtOrs?uQ0`|94I>P-)KcV{cl2<5)tgh#; zF>8QKg(!|2`_>P38n=+LeJe((ZJ*@mty*B86sM#-xrAf zXZTD1khjJ9WbM@Yd-zK^C;P7eMzL)tiNPw|5b(k$#cx+}H@$({q(Z2%Gk5x~F?gVG zR_qI44D{XC%< z{jAF4&EX!TgI1VOO(XT5?w*vFpz$vNKqh6oJhv@^I%MkVK@FH3N)>;h^tT%WB<6K~ zZRncMxo3;Y--58+!=JKa zeaY58>^#?PKGP96x?G{1N*l1$PN*fQT(uz0=bniLksv56jvd^X$W>F@m@#h6(!L|I zY4Z=A?>7@?aZoQ26;mC;jtV{bhTnepQI(K8#Z@P*U_xM)pIC)s^3*}y=5v5O$WgRK zZZHBa$w<6^480H(a9Hz?-r2PKB6%%MqEtrrBvdy%%7nTK=MIFQzY=XpJas^cj#?xD+g&@X*J<9LtyG&)izhbg8S>^BO53z>dM{j*508+aigcl zE7~wB^o-za(Wb#P&98*d5nPakZ(lL^@EB7&UqGG;Q0aeJ*Uq^l;*R5UP`!7_I4MW} zR1=hoJMlr;qvwbOof8*p=afgbe+8fnp9fsM{rUAAG>en=VoAN2vg9GWcvS2lHzgXY zHuKkcC38q}8iaUf5qIM8Kdn1!)5PpISdcRunCkYo5%uX2oFCvU_ zq&PqW4610}7kz#5(=(`dy^C+s0tn>$quOn^E4V&*b1L&1-{1lNYIu&*VQo_uiIC7A zW|$QqJ5`2l-XK+-^sV7Q@I8^_vG;>5ko!0#vjq!}t?P$_Pd6g@mOBGLRZH9b*B^M| zS8&5A>DQaW+k8uFET!LqM~MS=|DR%+!yS24i##i>(W_3!E>-&g}=jcFR zylh&i^wTRV00s*~pod^JJ}h%s5UQ7SXMwXw2#1L;76ebWHo71V?deI3WmN0MC1ksx7imHLniZ6GWiK!SRqIu@})WmLwH z{`q(?MOkKg`WsaK)=cZe?2p%R_FFBPpGjYYYFp{>xk`h$QK%^>uO<3G{83nJ3syw) z*OJt26dSh>GQ6@l_XZ~4nYvwIO(*Kqqa1ZmtQ7-xjGmRAAGgOO-Kh@pRG+t|Ec;H# zO8Z$aSvvVkxdlhWO=es4rfSOj&wy_D)EJ2BW1%*UBAU1#w+w~VbwEV)+<7LiD-!UK z?B8u)-KUfU=>H{Xt*e0wv%CuC^Y#`#>#``LX=z4ePef?EcO$y`bOlRG{%Tzhs!z}L zx-&xW#d5`DCjH8xnyPTWFkd?9gG4>-a z2iZECrL>pwUj(kAnk9o3H~mf*f#@k`+DT(aEbsT1JUnt4!ma`ZvyQHdV_TVgOfbKX z)LX#ylaTlK`H8W|JXQ>tb4(UPv6{*ztrO?|2IYG7jg0}DNha^y>a%AsTrk#NP^Yx2 zETcX;fOi_T90V~qK)D;yu7uD9+~z_&&eL3IOwQM7oSJA3#mIUB)D?qA6f+C4A(KbJfV2G`t<{kL|Y*SfXJs~;jq74>% z>a3J@d=7*bXZ%Y862j#?TcMip%Z1&%e=hYLC_GqWe`*Hm$(Qa_A1W_~wQ`~XiDL+z zKG;o}8XdRymu-M5U%N3`?gqsYd%;AL?62;FU=0iz+J=vi_xMee;_{({(1|vj?iLsx z(}%KYs|zMAK3d)eOy;xAev?j-$-;|Y_oVgC>js6-K&#*=ICY#QIh#_*an+miG1CM%CUhcJj3?V=owBE?>$p&o-gjtL)~C3 zUOzE$%SM>16|QN^#M85jsI<=YYk3`_V@Zvov1xu~lGj=amCODCYWgprWs1p;nQ^~9 zXhkl&lq0KWK+dIUQL;Z?c{oWR^LE~gyZEQaWh9a4XYzQiq_8|}AgqyI7Dy(!i8!@e zsLbFa!t_w!YefPJpmB7Itv7c;4Y@19m1G$BPL_su>;rN--SDj7rKjOg2B>ssY_i*P z6G=F;h{?aq8x%UIjpw}D^U@33r|y@IEX%5o+dsc$EwvUIjL8yNynR6Goo}NjK04&K zq~|TO0#T(#XuH9y2|CpMlAYcxZ zDeVw~6FRJ>)Zg>cPo;;?zB|jR3p|SuRMb`*l{$q;2b|_Av}Lo}Z%e5;7LXGptcOT- z#2=;Jj@>CFJUqxGEZTfcJSGI*C*!s>IvGC%hZgkt!%w zuxaXt+4rZzHPJxZ!&Nd5y#<3b2>>F#1dw6{i<=Apb~uF=jJ0$x^Mo`D6bI{>P7KpH z5xxQyZ`-C@A_l0Tihg zj!`q~gE{EV69s6I)PXFkW2IoG5d6>R6?eweywh)@6OgFk)26S_gSzX$sV=)gt6!KXt=fP)_Y!A9ivoj!Yv!h70(NwRCV05~yq!&)MV$Am^+^mEqz-R^bw> zCSVRr`X&udn?GPbS_yUNV$W?r-pfr01Z>wmnbHQOM@8p~-urr0yA%wq5`-2Elrqj5_85Xf<0ovqAdIU78>iQ79v=?8S4AVK! zUYoDBEl{A0@|*bq#p#doo67lS?HD>MCN|$KXFn{;M3s33;{s^rEx1L#KTj&W%t)zx zb9nq`qu2 zk&^G>tqio24TG9^F0=ORgh8lvmjltEUckQuGER!T()7kSFP(-^3@L@gKmldNt*H(O z0wP2a3NlcB@SeF&k{YqyvTtv=2Xn&P^V4DYnJMKd-h!fA0AcO-&`-`APL-MmMB9@n zV1rAY#_6``1+vD^v(1K7*!`QP)<%&Bhde4Q@*ux)Vy0-qwia^TK^vN|JL*-@!NI%- zirexdQ?Oa8-{;y#HiFA?JP|x7Lx?~y#(!J@9ySA|yM@3jbAV7r=PsV{6*tHT3=)ko z9Pgm!yx$`rKR`LemqvodT9I$y<-Ky>K`2U)SIgqcKGa6OIKrrUhz86vw!Dj<#SKMx zf+h(O-yTJ&<|CYuVgsuteWjpnlW=jfw^XdLB2H+w`(7O|O{$-%hdscVm|iQX)&`Lw zHRY6r1{G}?IOxeiOtUz!ccNmxUG)N##nt!%q^r`uh#&UfmGZ-DhaP|48Ky1}gZ!Q5 z{uQXc{(hWP8v}6>APec~v2D5CmOOiR{pGN)D_tt6i)<&eIj}xWs7Q148yjZU9@KIb zXr@YrU=`!t%(b!$v5b1Cy7)>XLgV1K0N@HkPiCb-Zeb!P|ZttrewF=Ia3e_=mffFGzW?Y z*>PWY=$V3(EjABG`zwvgIj36h;h_{>JjgV&rQD2v<5%eNz8bzMZwcT`w`OYl#hojdlTNPQxVWu0S?`z+YT@;{Q+^>a#*Dyh)c7C3={KZdGPoWMPa zMl1=+X~#Bpp<*Yfv$p^UP2!LKgrQLY`vud-P)WHN(}AA5?vem*a`;-sIx~OL?OtF< z&_h0vt#gme?c=1KFEeH@CU>DT#x*Ty~*j%uX~T#f0o8Mun(109A`6W~NP% zW{LN8#1Q#M((7#6x*oWL<+O_RkKxs(*Ezhu)Eq3qgn7G8TKo;SH-~|!#Pb`1luusU zpF9!vfWXzsf<3*d4{!``-}i?|ePRnXH7jE0<5;DMCbPvjiZ zHaeO3kycy%0XsV}1FDXX-4I?o%734}UxT0!+%N3%@xI>;kb)G;KTl6A@C?@bj_2%EsQZ-TcFklpGO|C@mA*5e_k(3@b2DBwUbV zo>ika$eZE}k|z>ENbhcT0!@sVO6t&@x;YvU<1xMN8oys>^~1?L3*vp#i4WsdPXr@Kl}c z_1?8ds1PK-8utibuF+{A1RM4SZITQC8=%&?1x|K_R24+hj2+Y(l)nC{D9?x9?r79*mo3VmOcB` z?K=PaK=S9Tnr0v}D0Ih+5A6VO=mXC@^2{ni^ohXdW#c|%L};GZQFrY_qZXh`53~Uo zJ}s_i?tT`;WuAEqN0`Fe$RNt6%)BrA!*$8XFLwf!YsKA`mTo5Jj|A~{4QhU%k5L{@ zEKvJifP9E_K#M39Hvgf)@Tj0s-FUx&CIm_ipURwJ%+x(5#LvVm9Y`}i@qaR#r%yo< zrev2jmT%;yM4J*2Y%DYYdQzIAzf5(l4>t#h4sFUeK4b=q8*Z{7HbSe;O_}IiU-og z(7tqY_+j2{Nivut5?KVp2#1u1A;fwUBy9oH^akV)#R60OI3<6sGF#5KClpHdQh_iL z(9v(i5Lf`1^A(jJ^E)3MGk$n$+RvF7(eri(TlNvtw5l^DHge378#>M&cM)>nD`;s& zK_08=7K3O=S;RNJ5-aHhcP6Gu*C;2g-WqU8Qt+!+8=V&E5;jw+9ub5PMhDx0S6`d7 z91~+iI;Fv+eGBnLY05uWfbBpX$9tqpW!?ceiS*A6)@BwodbG|U{faD_)QWciZMqjX zOEbv>frbN4C4nbQJ6 zBDAhW(Ag;DF$;pww6~m~deuYPh^U9e+HNHgDR92V(~3U0qXVJRO9X(m|%%Uhw>5 z7;%;~(kk5pGomCl6W5E(YlM0%c##iKr#YbimI1HRt|fy$fgiR(V1~chN1XUn3pkj$ z>AX`svjW%NyQP*RyUi>J0PYp@RF@8aC3mflxubdLju6aPEleKsnWWpF)wwmRQqz={S`D|)Gc(cnNKMAiA?4x@paKy%rKUImO{;`78TD5NA*9dZo)3OuG*F>uHq%!7 z1F4Ti2ZY{)I>5&)A)~)acXS5AvIk5Vj?ISbIeWg3JR2VfNJq8~km7UwkF*xrfHJEe z**xIGnqJ7Y3As8K=9h_1=8%E(u$SZrm?(w~M$LEeM{oSgDZ1=}T!G*`tlItMV^j`* zr7L4y5>uclIRXUvS9x;4Pvs27{rKF`3e;@R5!gF*nlwV3EF0n!=SBhV%XmJ{iB+E5 z7GwHL72UMieLycP<|j6h2rZX z?qM$hBA7T%N90g3xyZp`ZCNSb(oj)~QVuM`!F08C3kGKwl9UIRfY4bEr2>T#&gCn| z*E&_6DpOplJ%>}f4z8IbV##eV-X^4?_XRWS|Gtr$At&Gw+PoRtH!?@ba-Pou4UmGg z$2m+!xwCNu+{J@!eZ#Zp`rvHS$j^> zlL;Rwr8m_dv(pI$u!I0ED<#Z!D_(~wpIq*67mW1}PH%#krKasa1C*IVETgM7t{hXA3j$e`Tfxy9#d8BQrVyf* zg9yW1CfqeK~mL<-y9+XUP_L85DAbWd^3C|pw^xA&v5HT zHAW$Z*Yh?4=5u?;a=@17M8Qpag&?4kN;9fO3A8Vi1h9x@^|X(lv~hk%KYNw&dhmL! z&H4usCpeeidxx1Yp{-925rKdg9P20}vdyJe#_hqtxP;g;UNl1pEC`*=)Xe4wz*~4U z+o}Zs`nAAMsoDB0aSxDK-pvJoJ1)!E8PM&QN?cK#PAaQqfxcc9 zDBjw}$_~lKSgyAk_C+B0R0V{gve^*1Qt+RaIh~FhX@6Qe+EAvU#jOQEdH%rP_}6=ZK)4bfIJD* z2R_D?w%J(&MT0J|&dXcr7pd{;+2FLhe7@TgxfM5JtX&b7H=8O_0D$?uT9^#@n?+m? zfXSSK&LphN`Q|f`Fu=Z;)$hz+M?jQroMX3}pmJ>_UHMy46aBZd>H(_)1TrdmS^Hex z@`8E)ETcqR(Jl0}o}}6W0@@Sv$DEQASkJg~&QdR_#0$}kGz5@pb%NMn95qHnwYvvn zTg@zr%9u~B$ZwmGbD3A@OEPgpiog#nhL!?Sbsli`&ph|zxgL@tI*@UCoN5G}bIN|- zDF>FSy|~dqtvzSE@0cC!s09kd&ygc@4kS(nL55KjuLwq+r|1wMdb>xt-r0rfWNQel zMWe`W7mK40J{$H|z6SSf?Y+3o;ldDtNQcOV{#Zt>uA{o2j{Upj9xuwfctJ)(tZWLC z1Ry3ZqtfnqwYk+f0C&5J2a$@}KQFq)6mYo|p?Pu|567bD^BI_wXLnw=eSj z_{6Z)dW@U}^_5Oz`qaR8u)07h85J7bP>PG+oF?TLe>EIxLvkVGTpp)RC|iWaCY7DR zPE&{We+E;qGQo(5iXmo0HaTE%lE2MH4T5cL>B(Emv5LH`-uT#u99ZFU*ID7*rF2*} zhqD*V_M|r`YwUjpcyk-dJFHYp%Fs~dvT_wS6Jv5pDb;?7hNPZJ#LlrBj4rb!i3^H> zNO#s9G6?x%>f#o(2ck_B`x7IE%vj_z9ATLm`Oj>uvXEO+>x6h6!hyd*%D3qQa);7k zxT`_xre34`Nyw9x#}TyPP!B@1LIyP}4-ZsD^w7Zlu*4F>=e#&8Vo z-PVh5+)pCyb>WSXXB|VfeUql`YO)jvT*4*g`{p1zd_B3i_SMcs=r1k}{O86FixXvO z98)MkaG&dd`psAA5O;}$K&{@%j;3$zcJ7FwkY$nj>nP$-lmWdz2>M)VwUi5L7wTZo zJO$AEbLbtT^3>RON*1v@AR{IxQ(KaLkboTDVL;#k-TemdU(CC`pYrj%+3(y{ig*I=&u8aY2s--FQ6jnXA zekb<9n;PEpF{g^S~zyRr}y*OIW*Ou52WuP9eUG(X-lYf)-e><=ucSJOi zl72r-k16RWvIpakdQ_0U|2K;V71do>uW-T4qxEMpNI@~*jjF&eSVrzwa*!ZVP%oE` zzvuYvR_teUQN^lH>wWBkT?ZbR09Vk5=;C#}Tn3mANj?!;*d^;fk3Hit0wCS)FTaS5 zJ_7)6N%vn1VRy)&>T{B(I;6keibdH(G{FM=UOsXid!+kzD;t}*+4h=4w=6J!bQfG> z{2-Qj?eE{VijF8MZg^mL`XYA6ul388Xd{*h9Ndik?yt}IO|3u;q0gbFsopLh+6UjU z)F$ap&Ht{GCwdM1=LS4dBMLsY4A&udY=<4xcm4Dc981pBQEc+2Ab*n-nEw4A+vV>s zT7?4UES%SW{7jlz}9A}U{d2QzJ-^KxZaXi zWWANj+9y{Nipv9PE4fyKPkcJ0vgJs^_eEV7Tkrf9z4@6JBNNllbrk&V(@RF^B^Ug<>~CFPS&zXoM84r&?>AR-e&ZZ1&KdHYSNk4P z_I%^`tqst6Ln8_|XlQizpuX&0>_t8uhK5Fjs>`?O*Y3JJfS$%ioakFz@4bhO=C858 zEGTNvu}n-z`2YBN^LQxNKW_ZANKsUhvZUpdB2+^5v}r@d80(NVhCz&FY(boa4gx6zF7mF!OhI`BfB>r?x`|(CYsC4-*@}pzj)XdjaznK zNay%tuInb1l=NmSH@0u#v|Rvrl6YTu6TjnsoSTkv9{KmPzyR!_qyfh$>@n;M#Bb5R zV`M$X#e?&ok=IYEiPvG5 zpT8t3$yvK|T&P~i4zgPdLKIKSUH^E7|NF&{{Ao>i_)as&p7_z-%Q3*)Gwe*R=;mX5 z-v@e8>B2pCfz40;XN+&z-o`f4cKTy-=jM05qrkYaWA1I$+#DQ7*ef+yDXtlQ-{$Czw^JPBm1DzgE9@e67j`pY4z}Zkj>f7pOZ(fc9UdYXD zPm@hHKiRzh-wit2;>ez;VNqfnLusRvgH&MNZP)p^Wz%2w@=@fLjctkkP$<*{0*SnS zd>mM@m*!*N^HL`Y$);JiT=?=)1F`2TC?gS$AG!8?M`awdvYy*X28)rHETj z;hY}$M+-M^)B_w+l|*hlRpxbH4JwH@b`mZivlMZ{Ojv<&c2#&zBx7icYWJ?`REDI4!P=PP0qMG zDD>yb(Y;fha7m4uvtGq&fw4Ycln4EfyM2d&x!0qju&{e`e%>U2rDHp~J*%F@8R~4O zzUIBEA^%~L{PZ>ZJBQS`Z)~>t!g27DPfUt(7N47yVAL(d|pUsOd|Mv7X`%Uar;pVjY0(;O|Er=f4{L;-Fc6(5H zivQIfe|CE`ZY=EOT=n5H5-Bf;ZV&$9zxlII?}97_djm+$R6BETDp0bb5$d| z%i`7k_8o7N0Zp9xG|Bm~e_4O>*#5Rr{Cg=^H#@rRbduxUw@WFf!q%*zo8#`ct%vu| zXpqJU(=J#HChz9&mXVQ>2KVnj{PpYC;fcfl^(wb)|DTa_RD|n~P*5@pJ8>1wY5oD= z&6WP-+o`{Q@_+t%(+b#q?7`2%$Nry|3{oBjFyf8O}NJaij-J!Ym&?&Dl# z%icxi$67cq@8~Yjy|*lKB{zTRU*`KrF!=GZLpOB(-bL8<>ec!(_691yUN+3{2G{f`lw zujTSqa36uomV%qVv^j$Q(Y)_c;K#k?+I_|@@UI682Gg^+1-&I+W3zAm_dNtlfbYzFiAY;{U_)Jy;4f%%v_-?k|h_r_}=Bj5QRdWVh+A?adB&Be*~4aaNXp zjElcI`e`&6LRNX_I3eG66ZSD+s^zieUk$$Z2z-Or>8ldwCI8DPJG$HmGBca4-@WCR zqyNwFOZMmZk2pH@ z7Tlt__8k7#p(rg4dM~wBi4zLHQQBf-1!Br(^a)O!Y0sWm209*5!&~1f<)6I(PZI<> z=d>n5^KAYt+*)TtlyPm>drG}coTF{66S%$}@RLPOM9BTG=B%~7co z^X&VR_gAI{)j3HdC0yvImb5;X_$2?r6H?pia*9%v%}Y!H(UInRDVE2 zf^E)yD@t*lGPRzb-lJ4qLkT;e=c-a zCrvuwm(towvk2vsnoMcGF@JZxHc@^09@h9=PJi|R)HIT|&7iG#rMecye{XOFl;~%l z<5}G#n*O63z(sWul+p;GNVTE8)v7MM5ag)a#HN6+9Rc{khlH1*=0Kvh02HIX^b(xK zx4(tTI#pyImb2{!Fouars zzU+;Kbd4T8T{RmJjw}Mog-!Me(PE43YzJSFi;v!@41lW4_q4k)ZH`pdzy_;p2cPKJ zVou!SaOH^%TNQ)jDkQsY!N}plOuh786fH~0`TV@v7Ooz4P0TKts04tB+hjIZ(BHN; z{SKx>j75+1<~w*3%-$QTHIwgbe(5p>yeGX#t5RP9!?|uhy!GTEPvc)72CQv12;QXl zpR)8aL<1~i&r=UIT3gZ3h_#C8Y$sJ=salU>E5?7sL5y2mz*A^mM!CsAJ8>lHgZsGc zlt)huL_$VVy3l}0BIJe1nVX&KTe)t9P)sm)4dEj(tFhIue(Dg_+@c|O(ZZhIUYex| zOP{j?O3$fcTgSbs?6`>74`=bQ^*pZ{6Ra8rym}lAvR&5Tgf(>%(< zW0D(dN1TI6StewFA+cc-LCYWyVsjkYcn)CY4=f*6_4<6~cS@Cr9poV*leE8z3JR1R z`sdw2-XAIGZ8Fh!NvDD5^lx6kf~_P-(yI($Q}BTc7;UQ?h#TX}0sY7(}oMbEX4%Phb!z88+&V#9J z#fC7uWb*_#1Ly$Wef+^1fId6+6}c_~O7o}_a%J#q+@;pODyn{Vini)Wg}*kbE!*=9 z!177r+at!=l-eYdOu?bE;9Z^^X9dj%FHeppP8&;sZB!>7QfwNPH4%iLpJz$5_=S-? zTJW@-U%yH>btfkYZ**4?=?GKmFnqT`oaoXGoq;zk5WXG1k1@lg_N8#0!L= zG|4@%OGPP+3fg%ohfc|9e>=WjDNwVg_Df_HSy78hr+T$x` z#*6C$fIxZUGotpxd)!>e__Qlcdxzn8FEh}_iRH`-FKwT$8oqYwCopv+K$eYYWw#3@NoN# z3UT`dF+=?#*cL!J((ld#X7NU{@h^>%xzUhLlg{!^Kv@v5*_(vsQEEb#PN$*b^e9;RQVoOk5^K=uHq%ZY=x8dmBJi@&EIPkAo z@&+hxpxrE^$A0HbB!#EP>HNp#?a3l4e;;qZNBoCZSjN4ffAn!iruQ({rEpCO zDhgip8b|OE*qf%k<$Wmc8$JCGfIkqr{b$Gxa+`$1)jBiX(l6M@l!Dv1&>iSbevL=4 zUThAdguxoimS4%;O5{;b$AU$>?!d=0Aal|nvrApTSEoVX47bOX<+CfAw`*Sy%2iQE z(DpIpnKt2YuS@;$J+M3n6@Hs|s~$TykEKqbM+~q~b9vXYCK1>0xK{hIb2OL1 z!A{OtaGX&R2~qt-&>2z6*snx`+!r#iG6* z!)}de(T8zQ$wo`OIk%;#>nrn4z|ICQC}r@KLI;pRJ3y?!t%?6;JPrueF66J2FD5Pk zg+d0*mULzLD!V}99<{DHdn3tX{l3Z@`pqW=g=6^U`M~-Wh{_xf zhxYpKE*qPjoVXXhF#7&2cX~&AEapYiVTJDpE=rG+YET2LH@u6hDp;e!ddZ5|kt0#r zoy6NE9-i%Qi}R;GD8lgd;uVCp3b;|Jm`APw&1uZ};L;N27P`AWRN(A|H7#dz3J$rU zoy8!j7`l=@TnVj#?%Zf0Q;lkavW+N@DTsjom)kxJ*Y-V3L$CRPsSg|LsHotYpQ{6i-SIqi7NZSF^yj#AGL$DzUc31^fy|+^DOn zGpq{=$%aKg&tykIGN8b@W9g#TjD^qGF*;+WlD$;Yx^q)D&W+N6O?A)f!*V!i4($8~ z3Ay~tDk{K^ktOL2a=DKuZJ@GJWnu1D1KXD&KU6@%7(iPb*+{C>f22BJSq8u%r`3h= zG>zxTv6iZXvM@)mwdaxPfKTB>jo0&;zlL+u=W4WXPwN4G-cG>ohvOsrQD6leLKB=chEcQjb06`NC?KPe^@oAbm&Bq*;J+(ia^S8QBAvWwa=G8IXXt z@c|5dilRAC1mN=k)Ja1cDU0SLo|yZn^|;=cGqAzKJN8^4gF=lXD5(>+Y}@e+id9Uw z{esZ1^DcT8AcKk~CUX^ffQJqa-j$tukZZnTeeTX0fPrfhGr^A4+|;Fx_MN*$FfUhM z1mq$DuAtyzw@iXSW^^r&ddONx6qyDh(UZH=lCchHvvf3RUKiv}JnZ6H{AB!W1!kslEcdWW}5&@2S0X3*{r z^d`LH$08?tumK*p; zxrtIMhq5^ih*Dkkm9ORW-%A9_?clpR?9A*IjII##0nq##YH{+gry^ioIgv?f5``aO zWKeDYGxl9re_IZdD2c+3=f8w$v7#N;1uv4yfDV5ZYGG`Mv<65{Q-E)He#)o&9DVdA zeWh1a8?C#bGz{?LZglb0*E8T7WDu$IvC2+A5#OnC(gKIs z=b8H~6t~2(X~gfzhckDeYo7i*^1+PCb)TE01hZ~F8|U`GX{?||^D3@AS*r)A9OnMY zQH9#6sf~G!y(PJ}yk(5SZ!q3nS6EutbC5cvm6d$iG2&GY-hj50lmE$~^-6xMu%h$0 z9zQuxDAB!L?fJl9H3@03c;o=Vq=xsfqG)5gf}pt{!cA7S)2V+bBE%^DM7Udf7}Jn7 zC$~6nrdZlplHmWgkYc|S9rt~dssfj~`N1dh)15Bky3q~h73{L@V+!NWZQPpoiL9nC z-N`RLHI;p~jHFHyxK0rBUykc#Wk6yWB}~l#kA#7f2_;@GwaC$6@w5Jq3Z(r$dXTfz z%`8CNDOYxBv7jrWmcQTW4tfb;Ig>VA_#A*0k1#(-dykkS+6{>fUSL>PTuI=`mF#Q3 z2c$;)?PSYlu6%xZMGu};XfSiB|Le?9%@jBugc2?dt!VF2$L37rOG4QM_eH=}BA>4l z9j=BC>4+jb?t1`8+qd0Z_T_-ES>N#im@q1r86|)amJPP8al%@dmI1^(vk6g_yU6&M z6jiP34=NX)?}K*ixe$CX#Uyk|_@&^3PRaxuINIn`z#m-6#=DJy!w=6^{=*l(2CrCV z4>t8V5h#;@ee#-6bZh>-JiM){ER5~-UJ7tM6vTq}&0|VE*CGg4T2XwN4^X&+d~4Tb zE5Z6ifgcy$Y7lLD?7V#NI>7G8D*6G<9S{eJji%LT95_CI)p2Mg^R85MmW3TIt?MOB zZcpLZZ@GbqJOUH^wL@vt(CVzP;1@OIix(;S`PQphaC2Nz(&~pvLq&I;A7}53JdXqz znFbF)jj|wMrs)CA&?jw`3R2fHlic0g-R=?#T}<+WB6szj>;($o~-r}{Ly z8o1~Yx5;x2%V*ugZOv?z?nf4+mI;PK9_dX1yRJMo;|^g=kBQA0%n!^*F_*QUnotIl z;0(Z0AF-wFIF=dpc+q06U-#u6>;afsk#@HYHt5~7 z1xA!FC)5dZZ!iWc6A$zUuus5Y!X-xEot4u8aH>ql1v;$ZJvTBKcWxaSH>a{cv>V9v zIJ2SNjjYwRa6w;E@oQoEdKrMtK>@mt3NtFiJh(Ko>wZ9GNwMA67yCRww6dCS$s*CJ zH_i&UaoKW0CnI(t;m(dg1Sd--eKct!=f+Gi7m&2wj6_@ZZ)J%;VVFET!+dHU@C;O# z-IGOGhCTaJKCd3yj)t22E*E)wrr3dH8t3>oHle zb2fe1NR161cz#njGOA18pu0~lBEMeTu4tups@J4vhltOyF{8BlR)3lw^#oG#C{c8T zYe;x!^D23zvUHts7hVaic`DD9NsYhMAqHiR`4I(V4o%Wik_RI7(z|u3@q-5Tr$@y2 zfE+G47(dn=hg<#7`LJ2JZYD6UobKCkTe<6b*t$(-bnqILo}N2UUdA(!`fC;|AR*#w zP)X(2ARBmhX4Pm9dD!h0CW0wFG|}sK#H*t9<=~K&yaE%Prh3CwNPgkpdPf0hz7Xq0 zlhx~N2KG~p0f&~@k^|F$@vhLDE}F_gM2w3!z*W_$%>hHY(ZH*%Qm0?L1BgXuOcv%X z&-$~giMd;S_*G{vm%oX^I7&_m_bZ6{RxlUIQ$P^~=T`RoNQSIlx=t3M9tduX1->v& zIyww^OT(*d;yD-X8&A=MpZU{nm*qR1&TLw`-jfH(7SeiNxk`b=%xEr&KYm`jw2b2+ z0vNYmpIYXXt*UgaC8c_jCrP4L|LFn<67IM&c68mb zn=yK-S-ecmB)%S^F0s=LJZ|YJ@||ZzeG#~Ul3T|OEG|ao>eLl$RVs|&x{@((Xr~Sa zt9LG_i28;LRU@V9mXlrPlS7UOpN&y;PQ+fInG3k9^#EoR1$j3|D4-9S(2|>i{TLEN z1fVj489)hEX>;{{z#LsuJp8pJ9A7}d@X%JU=av8^pDOBZ6^P+m)81kRserb_4T*Os zMM=~@Vk`NVjxL;772az+atcgMer`0Q&kryPXsRK$qMO%Bn>$-Ig{=6-9kX zUh05LSEUepkKsQh;j7lZZjCjB$9ZW-#tM#x`^(<@Vk}5us>9dD2J=}yOaQsdTB_I! zC%eoPgXzb(4oPJsrW4-}rCF`z*afIp*_j6pY@YX~wyyT2$9XWCZj{&?DaHy332_g6 z{S|QH4j#?Tph4W`WT&N9EZGtQpT~$2a-|`Y)J#KH_s`2*s>OVeaeYOe zs)#_UjmKL+g7fvx-#NM5=hI;*H4dv-?Y^(~V+T5*VoKSBwfd^iXSTVR7HVe%VjNyc zS6RQ&`zF5TQ(aLK!oD(pwDN+TK2*Se8R$0^zpMyzN_vwYnC-9k;`YO_Z?Td} z&uf&!vc#ReOTZ*%vPnSMd6po2o&s|;Tfo8Kdj>S8^Vss(mjuaJ3@XihpFTt!V)j5< z`E$BO2at3V6E%J)9o)~3#zxOv9DzC@ZqFDUOL1Y`pE7-_SovTakP%GSrr}v81jKX^ zrV;z(V=Trr9Ej&GpGX`%(}=s55{r2%x&5#i!@%4wXYZ%VL=&Yw8V}^*6UjAIb{Yj( z*T`WI#5sfA9Ca_usP3gY_PF_`769MtkecT_c=qn9VDaU*E@`#&10@?EPZ(6kay#2I z(*y9Zj%*5DpMM*37^Lt#UBX+bLNr7FCx#8{=Tff}VHSET0|GoNcpAXGQDasWWoBjC z{#Y~-bc7_3oHN4b!sl3PP%^zA;o)qy7&)$(USYqzP z0_tb3o!LxyVD%@=aT>QCn~sZ8jD8kG^hYnm-;YFwr6ab_hNa}B#LSSik7FKW#|bSM z8?^fbXSe&eD{fy_%y=!T)TZC6<}0?;)$Xho0+O3_+f?91=*Q&uS;@I0W;mQqi;oS= z<1xgZS;j8)xEELl5>ZEjuYN1(F@WSVX`e8bYyw}EPdgC*G5t>BKMJ(5>e6`Dheo;4 zHS-JWZGi)DvsgOJ3rQ;_~Cwb$1%*RVGN~+s{Wmx3hCp?g0VH z_xT&~!>ice-0H4c2Kag42Hnz6Y&eMV9Xb#XXXK?S(4sZCI%C%Q);dTjC zagqIQ=m2#hA09YTK2&`knu6-OI6?@fz_b5;&WlSmy(Uf`P8wPpJ3C0Qo8|3C8QkC z5fS}>`(+0Z=yXcKm1k@jmZo^6{Qe68+7TH(hVxdb<1$u^ISf0#aP!odgy|zf` zWHCRRUpklmxwAT$m)VIkuk=S4A5+!99Xz{m>cEAit4*<$7FAnm{pE{0_=3Fzn-{l{ zSRNl>sLVAKO3lDx7O1C0R(|J7;O0Lvc|^)0-VV66SeGq&1O55P98Y4B=KSXNHfvI0- zOjaml3s7jDTvT!!Ey7fXPU;D|F*IB|4`4cI<{(&vxpiFvyX)2AkqE1z`bx~`#3|l@ zn95MZ&lcppE*UOGq~5cDJ#EThu4JYBmM;F9-<-Fm>%LRmE@#LxG&LYt$tFE*34|_Q zRob)=#AzV3Sa|H}^P(1N#{HBD{lgx-<>Eg8H0#f8n>h@RN6rTiR4YbtDKhNq0SCOdPE(Tl1Z7yH$vnYp{`|ZQY(ev^HSERm`i#PJ? zuKRX1RVfQ<+~t?$NdtNc`R4TYX49lV$d0x4dBjRwP6uvvy*;+RM{XDq^*sW{D+yc75RM zOF^kXWErqM|2ddTALr@_^xmWG)p;z4iK>r|q$%LDx~WxqLo4u=jY7-l1x=X(LiK4@iVm+bN(oEs3q3KrsO zhq)*lY7u`#AzpP9+G#@nvp!uP4iaV**TsD z6R7wgWZUluDT8^g`!VXq0Ve8b;Qy(YU8UhcExfyM04 zGM6sMbrO<=nWkGJtY~${N3suzWV;_K&z3w8A*Moth%lsd9K;~FMJWX=kG=cJ>sj-F z23RtY5W9HAz6#om{W%SIt2sT{t2vUFvzqPFudyzJr2ApLy{L8S7B9yB0R^PgTdcc%z07xm|fzkA}oVHJO zAdexXF45*Jd(#03G)L0xO1>#~zF*uLjUNVCiXPLPS$4&>&XUMO=W$6z+ z9oJVMu|{>q<`6gL?RijEHoBLffHE_qu6KnAeZMH%au~NidwDXL(8OE2qhWm)gx8?G z@LSI5gfq?%rvad$Z5psp#t3XkX2Fb_?;xy>P+WpSRZKZ16Wgz0vNE%Kj=7pp&)A zr1y~Z9BS~+rk^VdWu$Y>BfNV1OJ;tu zXTQaottzX{iZliVoqG%2yf_%@)7zBeYrBD>!UH_=Ss<+!4v-0MIvLEDW43CSRJ4v> z-3rQ-7Fmjv3z;cW3v9JSiL$WjOM-&1B$cYPd#|etwVgm|7Ad2gKGyPwxbGrHAhse_ z%|Oqqi64vV6xZHjS_tUhy$4YG^BDgfnr9S;12jOjT<|yjz4xYoe1&{)$ww16@WEFf zApiA|UuY>8T6J-!!m8AnqoWY`#T?@d4XNd3VwtNkBk3C6_beZYdP(4u2 zuZAx5q)!7@Zx>AFTn;ynH(R%@6I(3nE| zwuN#z>7cB&s9Vi;yEIF1pwk=o1Vg#1{Z(-(JL#x2M~Lw5yolny%WdtuY>v+ z#Z`o-&4DX;R9CHVaff=&{>Luup;0Tu&auyVQMGXY86{u2S0qLdaaA>m$mI|6XWp$J zuWPm#E1nx0%{G*V&GgXkTK^2*+^Q3esz5skOt z@LLLK=vidsrAZ_py~cgh)t-m67AKPc4h;k_s00_6i|eYdVL8P|1bkuABC?+ovU6d6 zpE}ZE0ZgP`z`DrUb+t23ur9Npgf~va%&Wv)#whHD2B6h^@QjI0Y%^<}d&r@qwJTpg zh#0f5n6m+h-^HarhJc>U`QRkT^_~rd-^vv;wifgUxbK0SRx>!UydLKCxoSu5qdrx? zhBxNFd{V*`E|lkdgAX}lU+}dmYR#+w*#kcHpbTCA?n`1|mqCjQW6H9Yj37wpeC5h< zQ=~U=`+l`ne0DwTU@Hs}(jQ?x-~)76xIE2T7Eyj;;kK>Yzh+<`<${! zMzW~L=m>z&$}ZwNX@Pqp3^NN&L^0UKd@0oNOV!vR8i8w!z%H z*}r1`1_9^$HOv4%evm=JM$e@-l@D4gRx(a)!_|F{3qrQpC=kgTlJrZ$@8!k)q`Lzy z2)q&(26a5I7N<5w0WXds~XK+VtY?rsg08Yxvhl1GPxgxI?|}*`U0|%*C!2- zp$w4kFkq0{U#IG-TfY)7BTLg}LNrO^#DK737)FlJ>maoTKmj6srkX@hM7w0DMT3`q z>jk^oD8@dD`Q)av_yz(M;dCJhg`3OQwSFL{kC7JDbU?k~s3pJ?4HC(o2EjNF;+kpn-~1jsPOZqI%u> zP5`2&ncAf+<;{O!J0#My5bQt{%Gu!~UK$VfsN1!X`CBo{3;<;OrBloAuK{!wrh9W@ z008e_G1LU(rT$oF0YJ3De9i>`#X@mAX zrhv_F^toF+-`&6$(L6X0fJgfl%+V+Hdz4BtB?}Ndu?kMHCL`Qvq~3#wBWitRUdd|^ ztU=6`)>X$(2W6MzCy^J|r4O&#?xZ9gq;!~#7D`JMj?YJRKP0fos~!kvU9V0w-FNs- zY0Vl{5%O%ht)g&hf(zYQuTqeU<)%L}!BLuyLpnW1G87%>aMxKAQDMLi7C?KH{69mO zWnW&ZxEJ($s@Y!8GUoCNSA2VO#%K$K3)0y;T~LZt@k4SIa4DK1r24U0@5qdT`SQU@ z*eqY?;5x|u*}S^ZwL^F~IMiy&qKa|Cv3AY5__~^NMVE0_u@0_te=K1lf0u&uau0&S z7ogmyyl3T3O?h~7j}7Cgy7eu*IejP%2eovf`h)fYhu>=CxxQY(cv7dskzJR zYZCkSs6%BuYMWA zH1H^%c_D~k19Pi_xViHW7#JYi4V(BguA@zGNco)Nn!uqaR#ziW4Krl>*WF`lRc@56 zgmIxi0Q@u?7P9wetUNr5F-;*)>9qSpoVe&nx65TMu(Dw|3q(Ibm<)gjHA|oxTqUQ% zjbwJ9_0Gd@&nTH$C1juHrE7KBLhSrO-MDYkOK=V-1*)rn0Z&mbVT~(y-P79{0CLla z$f24LGSEui?}p@h!d&8kr0_x2b>)}iZ_#J={H{FY672!79*ll|eL?f&KEtdtRaEPo zjbdS6l3iHH5e#!T699~(uQax5qgB`-8=;A=3x4oG$+0!*E|76IAMeO~PxD{UHi^w7 zDwM!h1_qP7{b!6eC@@n~oKg0>JeU-VdQ`D}&9!=$M6|l%iZ*&rLFHH!dv=Jv66_`c z6h_MUjSH1cF>-Z&9t-00BAm0e0T}!}$`=-RH$S0bqg0+f`S&55P0D@U zTBquAhYKyyY-m9bP^(j4UzCWu=$1^obIbK@n275eH93}w2y5Y4Qa}#6vN9{{Y-xOsj?Us`gTc(GlF3w!Xhc(`}zc zHoCqhpHfiW0M0;2)sdYvukKbvNwWARIwdRbTSY)Yb3kSS;!)i5s9<7hd&@)8(3j4k zrWp?~Vj#uFg1FAxYdCu~=R;-^&yJ|;=r#IRFpN!DH>TY1ec01xqmU>aJgB6R05W#& z$`tB|S5}=(mv?78cZogI(0xf7Kbe{yR2Tc`^s?x*54}~_@D^1DiFcaV203o<;wI$m zuslT4;B=ZUx|cc+B+hKu^^USm$~wq-k%13F?2fKm?n5Bjb;j6cuqDrWfEtp%Y+s5) zynnP-WH{3ytv4-sa2}-A9RW{bkE9&C8a17Z*V8*^w8cg~VF@5x@}eqsxqv zX_Jz22V1}n1<{L88a<@q~)h z(Bw<0>4#b+3bX0s>8VCsRS?uYJdu=(D zt~C0~az;roKW#huS7Lasj?MZ)S*tu8Ov*43oCeU^x(kj+VD2IJFacnPN-75R0`1OG z5eMFbXFr2NbeEZ~{;gH8ME5CV2AICspPfrWn6{mSc5 zw*gT1XUieZ3YETGp3TnJ-lZ<#!{iTnz=npXOk|`4sy~-lzl?MmhI)>E8ATEYZb zYpDTe{lYs6pK^o&tV`MG(f{N8%O<*_M0;zdm$23p`fsjPn~eMC;_QD<03WP+K#a$7MQCqM@R=0D$JxY7m5~);pHnNhKJHd zJ+ITRW&XbPcRC4K^RDS8G-*8kCw|J5YvQG>Mt-Vp7*g{uJ z=W-Wfw47%B3I#_>64d&7r$4(jW`Q7#9slCV%-)w}lH^FeN{^udi1}T2@|i&j4ZRd2 z`MVauJvwW7(8hKVJ^lEnk-?;x=MMpX!&-t~ z-+J5huYy z8C{xOBZf5l#K6xyQUnA=xnJ^D6ZAzI0CZ};fF_Ru-9Yu7ooDvqETUttc!SUlvbT+- z3I>X=$KlM9eUCZQg+Tj>L{J}tgH#X5g4ku5zv78&<=zy@yc5?AP(!Ggfj(d zxmeyN5M~Z2drnU-W_@`d_|D%38XRf!Av{Va-+E1ePaS@DLG`}g3zw1aZ#9424&E+U z5Rr{jvZ+5n1_nzMh1KLQv62t#bs)C|SJ}EuQ$UTX5Tr5cjsUo)1!d-1QGHRO1S%Zw zJkWp9mM1S90I&45yK)u|_v16$hd}vhfQ`BN+yG!fI2{{^^dpBSm%6vn0F=`qu&`I6V+8kqW3 z;E_-QRIz#ec|!~ZVfq=aAf9!li(r~nKo)G!SXj!1^2eZCT3L;zsnXbu?d2?HTFWw! z@fy-70AL@PBE)vN4Mi}&_{Ee9bZo{zf<~q2?e8cRHISY|W2@{Eup7M{1^u)I(T+kx zdb`!P*XJqMMil+9&jPMSX~+r%5mC}>XbGUaVOC_)uJA)N~T&EH6{+<;CyWm^kMX7+^uBcfBv?B<~LkfLQzdYBYDVJMgX9M#$ZU zpSFXl%2g#E(V$GLdO*n-b5D_VVwYp{PeG5-ia7Ee0lRK)J*qLfd1^9OZwXHymB~%^ zEZY~ai|0a9!&Hd|%q6R?N*Fo+O5W>C6mh@~O8c;OnYh!l5c0vF$WsSP zC*Ysa+fR*}`-Nt$FPjl;=_=y6?+|L12ti=ZVmcHc=L18=752IyEzXr8lp7t&1}6g> zh`EvIfD%;KG1;a`moGpH)Did29jOZ3 z&@LG8GnX<6II*^p9Y=`=AU157IZqS(pus z@+0Lqj02{`wrC3Y5h{Re5LB%TEp8@#f1Wv*U#Wu|(DD{MFL)}vbNwg;XEPC0eQVo} z-7c>HlBMa;1?!vTm=Ko#fh2=N_5hmh@pO*OFFP>vs&hebk>^$bP#FuVxQ&afnY0-|t4IJe86m6Mj3*)U ziXCndsE`$XcJneiOJ{ouA!dw-54k$4twhTCDW6&ZpJqnsk+I^TXF-fuYRCaWlAhp< zq4rQITTBws3RIzk;tp75#Z8o1H?_6W-CrZ~s5EZC6Vh8Y5`@-B8$jk}=3L(NpvCu;f50Dv+45U4zH4cWx9XI4X%om#{OFpMmsh0*u=!&c`%Vnx>GnM?4nSC}^vZSHz+ zVY00Qu%vSvAXjh8ro)uF{uuaB!Fr(Gz^A@!5#Ui@StHnKHFXW$v~c+9Q}05T86!YgWZrq&rCo1hb#)uZ4rJvdKSFmJfQ*W3RS;){ShaEypAOJ~qp zTd}`C>hHqDQPvg#=@4#iI3P1M)g!lvoX3<0*Y;PxycxNtbj=Bb>klp7X;)Dp0(IoF zebl0g)cr@DfU%!LmOp8H#!4jXyGg!7q|h~1x^%$sujs07Qrzy(@{21$g*~o9&|0N0 zuVY=3f3^}j$RU!uJ=tNlOUquj&g6~9FHF+^1htghzwdKvYG`tp580WD#8p_8&%bCF z=2gc&_DD~zD|Tpb^%$~Euf>)09_&YH-wRdpRfWVFKt>g|L7~(HpXpti$kCaS+_aP+ zqOq1>3P6B1U=GUiD4aob^(%`Z0}BU+J@M;>)G4Y8!lj{JtKZS4zo^ckQ_4Tr=xPA{ z;7OBIRT4`$Hhm(GoiD_S_O_sAqt5sOQ;H$Ux>=I84U5eysIDBkoKY#xm2w0)!hraZ zxo%WN2Q!mr_nD0J%l83&HqfbB;FXXp{$b7Y;y3R#%8;@=;^<9}mL6FQT{ZvS@MdjlZ4f9H5KM-aQ zvEThM4r47@Bq()=K{Maz4Od@%1Z^VcjNzsrRgkBU;08-?cmg2m_5m66{m@p!i1tnp zZv)CCk2<^z(qo*Qi=dRq7nwj*y4X84E*+NBSnliaPq)HZeXNZs27D96Ma(5S zR6h&`p#m_EP%+%VYRJn>j!_ojA13uZo7N5lkoT7RhyXHV>9^Y6Q4s2fdha3Wfbh#u zFqtNH`rGTMMN)tea-!qMO2*G@$u=#;4dzMZiz4MPfY<7UUg+9j&lhC5^H+ye(r>}a z`nATLxOPhQ&?*SCzbSNE1U=(RUivNAkD z&FF;|A}cfJ3e~lq4Y(}`lN1HV@?XUcP=U2q6<0dbDJ4L{O{^1-(w-noRx4inF$;?I zj}?(;e6yW-@4e!CQyR0*A<^gE12OnPJ{~g;3o!OpQtul1ydXMm*9Qs*%>5TLT|^z` z#tpO%S0_@LKFGYVHTPW4idZM!03*S(kIalMOP%`2CE2at(ugLO0u^X_Fd7^5D<*c? z+In&V4w$e%^wkI;RjVxS8GEK6?k+E&Jhm3sutE$Yp;g5hxW?NJJ|_3=?Jtz(PyA}Y zJ?`xSiRB)HNyvTaDN5JvRopLa!W{>Vw?=a1Po}M%+*Jvg`ty3__~6A26)tA27lu`r#a{WY?2#x8K5F zD}UeT)YvJAf$TJllzwlI;bR+q8ZAvz-uIjxM?&uFZIOgT8eDz^JOK*W?01OjYoYCo zAp6yNBv;x2Ym)y(bHFGi1C%ng0%HaN#WNHfx<#qgN$~CiG*Z-VYmK$702TbgcvhEwX3$Mj3_*6z(4j<}oxL1IV|SP??isSwRBEL8>n+02Hu1B2U*LJ+YYBSpE$tj*lhA zl_;FfFl_tc6Up*<2w1L~!6=Ad=X`#u@u@PvQWjRxk?h*|wiuJuz+k1sX{6~SOn`u} zOn!wRo;72bg^m!-?CPM*Auig<6cm@O7F_VTW6|9qOy&z$(QlYn0No-Og7mU9N$U6_mqjFwW#qE*THZ2P5gvST@ZR|?OtbfcZUws|IJ8Vr z!I>cS)hvD=%!|wkz5ibuXbAJ;*I4E+Yr-z|bOlEM+;+QTi{dhcYk&`MJhrIKJWMr{~*N69jJ0ZG5QVT(x z?|q8%qkqLD_qxHyygutnKAY+wXa5w}5F$mpZOSdasbe1z;4bW*Jo0BxqNL~=JD}k{ z1Cjyp5BKF|l8sr{9t|Btlq zj_0!f{x6XgDWs%ODw$ao!aEuoM#;=7<82qRw?gYiR@tLMR<;nLMaG+Cg(%}~lfC`U z)$O`;e{$b{e1HCWKQ7mJjq^IMbI$8LpJ(^h^h~)tSy#N-cR;*NoRU2Sns(p$6pT1< zwSBl{gcYkJqo)cJVI%NL&OR8!7_Z6^W}y}ldn6~{ua`=)>D0d5#U6k>6@22UmW7eiH&1>e5p$6jxj>49vFcgV07a??M)Hl2$@>=i;cp<_ zc?cLBa^m9li9;Rf%v7gIax{L5Z4Y7U&}gAeS?G}A*eXdAXCn+YN!wwqy#lAp30zGl zTDQU6<>bRGPI;%I|NY~Q>zpy}kI{U%3=jJqayp5!%LpR5&RsH!2~a9>@FvG1G~;)= zr~jSXPOvR|jj`3l5CW^IjQK{*j@9pvBYaV|G<>DRFCT7qTANsyVVH;ic@;wvJSx4% zp#}{vQ^~y{yr*;K%YH<3UR{Q4H^>&Z+RtlgX_+f4EAO~>DkU@XGZz`Qncy;`8MvqzU(*1bI?ALEy0TrrZE|9w4MZ6dtw%ssU= zGR@t;q@P%Q^LHvxkp9tKF=G?-UGbOZ@*l-sdQOC}w_mOGetDjMWqV2lny>s~N^5k| zJ)9AaMZ(j{{MGK_PQv`~C4TSKM-|y0azTfC1Fm;fJt_cnoGh>km}=CluXKs~p((WFTd}6RW>n;Zl1JZY`46F?NH0#fhwf~o^}luq7n2S`%``o0 zJ&k_co&PTAOlr+v!a&J(a_NF2aD*$=rhs%Xu8EZ?c_9qB~2!=7=bdZ zG5Dhd1o`fpBb8R^?|{?@MzW);vgKa|hs_=50`HoVdxB|}?&M!wfD<;iMEOx&cO3cN zvF?Z?XzT-9EZ71dL#DAanq&j2nLLnLOW#Pmh047fj0$nI*~;(_@mhNu-Bnm>W=y@9 z={N^GccAl~S=4JNSErroGTd{LR{RjQ31E#t-;6s6%ABw7JF$E9<1rhMDd~){{BL$gtew9_kWD@U4((InC1c|56%dm8RWSX({K^d0sCK$t#yPmMiKe?K)LldR3Ppm z(rEcb6u_t6uts{){~HG& z8NQ72-xs+%&J{@+AN|=gguN^M7wy>FeP~ws1`<((FwBSCFYUixbRN2=bX^aVVYWla z+;^wf8Ka}kmJ1rCnXd|CYD7P|Hhdn7$kBC)US%jIz8SE8uw3wRz(#-;+5SpTKCMKr zpc?Tvk*MW>v6cIwr-yx;2zx5A&A;v%cIE%nnrlE|cK`l;U@BMhTI-k(($%rfIve!m z>nvc|b0EU!&CD=$-5mkL5wTlP4CQ5`5Jt#Rud#+;E8))`;(!#}xu!&*=OH?HKx&sb z+vZPOiCy@En`3Q!v0rSwFkSU!tdAh#^AGOk zQ)EfW$&kr-&*cA`vNwk$Z6FKyE=`?eKcem2NVn%_0v`4Gjb2%2LXAX0#(447kI<$8J1FO(W;dBW z7(D%tI%m<@!!eUC`q|9Oy(bF0Xa3h4fil^Xpe}Xl7RC!I8~$rC)_2w9o;9CDwC87>l+oi9;CC_u8I_{mRq;nN``k zH4@AC0_M{9#O%OK8FXOSgg}slx@s<=FJ5Z}_KVFLxd%^pgx=#3!l1}^W(9a`pNV~3 z3nW~EuL=FgG)3RN&Y+i{M}|n0=N9Jn7#)Qx)n_Vw*Io`fq%HFs3wDf;^=ECQ1Q9hq zp@&=B_e1D@u>oF{4%fpJ2`^ix+nf5)fdP|0xK(*;scAjd(LC6`mW?d$kgQ%=Obq_y z3}yx~OW62G3;Q5?@7jxT^2maJ@jWgJ{U0unH0Uq(*)|U8qnJICiJXj1KO~dD?BTVp zqQ5*skR%|%8Wu8iV#;^G(6tC|pWK9AF^YpmA0){{*(=v;*q?gq84n0WP=7Nfw&jw@ zi;o?g#Mr>snSwA?X!FE;rX4!nY*$Hx9sv0)@6(e~SfhmzX4Osm3fFl7c25LCMx^xi zG#(RygVevsm=BJ6?OE%BzUIGLUqau`ieWRi_Qcw+XG+jmF_9J~bg%X=BKcItz{Ip0 zjPO1C9Z^$}f&<9+MGy0-U{iy(5*=coEIxs#A@bYhhq3ol{vuBjJ;|ejt+YS=o^{@I z3)HWC9_PZ^Q;0ET>$kNCyRscI2#+vw|JSAxg^;cUdUOk;pYS04C1VjA7?QCz<|DM= zOlR&%{@0ttU?)sa3UtA??-pdCEquJY7ki2l1X}*?ypER4E==h6 zlYrHH#o6NB-S^nF8l>5wv2jn)D{IRJ`{jQSMA6t&{}_4?juv^!<7C_e>`EM{C;H&) z5~v`qr+1j(HFJUi5#*=<)V>0~>eJu+!ak@P&|pt`m@)m^?$qK+HMvG0xz0Cakx~Jd zHB&Hqpga4Q_P2JV=tr%7wscU*6DHLESrXz1j2_d6;`iweY{w-i40;5au zY}CZ+iuL%DhxpIo!LdMtEOnot?#K$^TjZP8DoeL#q#Q`~?dCS*VEx#@_xvW7hKVfEUUIfc$G2=CA50_9+fOoQg^a zoZLC|hKr*TyImvZEF zrgmF|hK2?_dc@w}-yaYXa`45A7w4XpreaEEoeu~iJBPP=?_P9Q*eIa>Bq)f1OLF1} zrqu2soHLgU0~WA$tceQx%L8%ce7N4bq94I6QX8?qx!$sI7Zli7(X=8*|KZ-OGdLN1 zpKyW>z4H5tTD|7K1=LO8wUB&_AHUO$Bn(*BtLoQ=^)5^cX{f9PwllBOK53W=7C}aF zY!EE@VIO`KEp36Vhgq--CZu-o{MSTSA>-uh(94I76XfvYxl6fGxuPRNgS(tbc45@E zSUa(Cr07OAp5lt**r)Ga*jW9(4{W@TJ(B`PsDfleVpI_aC)XbQ;}`BavZ)Qt^^Bks zun`Ih;7ce5(Lu#UfC=nde^0L&y|T_QE9!!Pk;Ql6uurFvnjw)LO}X8-F&EU6x>;vz zQiPlPf}oJlf&KesKG)YD;N_LOtfX|CwQ2|YEY_>vrh$zX#Ny-kX9D`@T`_bpP}CkD zW6@^ectE-NK5x?!s=Dun5|_D;llEkUpS zKwp7M8R!6+HuAw=0~p_Cu~%XWpbUb8H`#%XA{gMy(5l+OcK+MOFQ znLw=WYfrmRp^aXzV}}sA(U04S6?{>Y1(|>-4aYto%&E)(&R)?F!`m5u2*TPcal){H zos|hS#qP=2ugD|LHlaGS#pe-rk%HmMn`MoJm+I>30imIXckS905EaE|Y-}757N$|F zWX*!MejWK`qYH%A>RD;*WI*qtv)x!-_Uzd+ic(^Fy_GaJ%SOod`j@yWHp-ZsqPJ}D zhy&kQ^VYN`R{r2_uDf?MaF||=9K;smHe{UH)#TBwXuYu%06KS3!vt@SwWpAf&^8tM z`{?+b(t&B8tj@>k2&~gRhE$mMxGCZQ`VE{gnec8%cMkiZiDg0Gje(meeps1c{lF|T zRK)1+BE;7MqP$x$2kb7ABz?Ee>N@%qTLFyZ)4}S7KgR`oUrZk?9;PLhBP7^U8YD1p zSKwslXcOke>;tJ6{ahA-?qBP@@j9}jr&LpPtvv;JO>9b`@oq(_5<228R4*?qQoWUg-1U~vS3gqB zCctA8`Htmxl=j|a)H})}M6F4_wE&eihlpw8mRG#$VnRYfJ&-*U40TPVZHK=ecz)q= z2(yTZECBePLlRu?)kF`T$(BN3lkWx=-Xq~tPXOdd_BFVebjMDC>vtS%hj`_~kIMNT z|3Ojs3EOD@xo%&e>P^Ikm&6#BkGp{iOo!3arX zO#xq@Z95@1t48bsQhZG{vMJx1_^G!#ym}sTw-322Op8<^G!RG?;u=>OAKC-A28`^P zfWeh7ACkX|BHdmt%@4k`>WlT++!oRoDQ-JNN8?J|p&~CQ7Yt?N)oP%GYJY9)ZJ$%a zw7|rk@DlQJGHnKddBUrk&ge_xBd9HYsHWN#0FnofkKRiEXU8yo4MjolKAvQl$i$LC zM<~(in+*mZvlEzhj;3E z)KC|?AO)DxQ>Odt&qg^89L|c6EhPfa?CBw)*kh_ME+4S=`#zs>^SgBaw<0IsdgDYp zLX?E)JbjTK@NvbRoNLL#`#2}(m^SU8IbXQD6rXoyoDa^IzvUBa)>}g3h_J30vS3^3 zyW~}4SR3zWiEVI_8+ThZs4ku6|A)3Xwduvrwhw-+{J@wOXiW{ZAOexpWA!GlgK}Zz z^HYP7#fH^k6#(Ojuqjl`F;T9QqG+%MHp}u?mR(g=`^chmevCu@mf^?+w`Stoe$H-B zl`0E>q5EbcUA*sYbRe-7Wy zXEauL>fUx+Wm=ZwY)JWptPxs8dS_d;an0HzF2Dd_J#@_E3uow&Ytp{Ur-qwzOdn^P zHp$zLwDsq|r()!Kf^+%kyQc@pM;`;zLYT`@vt~A6{7*N7QWzAHPUUKrU{={)OUEj0 zU=l^arl{*N+?spN(AFbpe&c;LhS_HV_H3oZoe>P)SmgZ_4)U2 zDYm-Xgvt=!z+CfIv0UJ^^tuQoIn+A}Y?8mc)ad{b+kMO;$eU*OS7mxh2`KR#0Z?u? zEvR?TlO;daa*fjXq?X8=K?@{^h`_&gp$$P*2B`q?4RUH#*`+`#4L( zLRne$rsyo6g|DY=Bw^`3ww3x9@7z|i~1 zVWL;Gbx8W-kW{k^kV_9GQJ!o~Pur|KGu%ov+dn&`cqcm@k{SEI<|Jp`FfmC2hVPH+ z3f*3y|IfeFE;^Byc=9IASEl%F&U_EGf3Sc?un8M-fxqrR82GA8pv`>8jj zq+79hctqIJ%Z3Bj`_x@1C7IqUeGtLA5QkJ4s%x<}z|=+xNd;sce13K>g|K{R-mKm~ z)!RI$Az4)+*I?y)YKieH^|#O7-)9sGhYB}50M-|%{A>5I5Szh9QJ-!`XUUtL?o}0O zK|evuS3=gRe$CZn6<*_x;-W3P1WyfodC?WY{e^7l=nn)l_F)-nrI)8^6;{Ri?6G~4 zp9h5#E`JK)9gIjjJvTc&ArPKVfE7QZR&tbo+iG{j5xvzmGxW@71 zjxV~6DeCc7nHqibP+_J^?MkYK^77P6w{xHlda~=~1DRXgZt5@$15IHdKFZ-kezqjO z!*wCG&-9vxHeeP$B8tF}5XB21&=A*0;@ve08`vk< zetLk*@DU`@?sjsCZkh3y^lLA$skGeVa>L@=>z4i|mo~x(l+ZSS&W;1Ec6kWz)l<uMm%r?%#p9-4Q!QXA6riq*bzqk@Q~%T|VlEbUsS?&Ih@ zqShA31(pyX;k(Y5k%Epp+?1{t}*b~inwhc=d)s6!r%p+Kg_4MgqZjJ#J z!m!1`i_VjEXTzt!P&q5ynUZ?eZ*LS=T|VF>2+=pns^`fQOT#uNi8wQ#ug_P1bu$)t zmmBY>%7~tsse5Azxr-BR5_AMRRfDM(Gp4yzvMOh|OPFvK5HM z7efV4=AFHMds*qxvMKHEqZc7P`n$9aUtVX{W}k(rK_cbK0>3@v)mA(^7x?T}|L0}7 zjZGp)QrNFb9Q56F1q-hK*%d|nRxUHQ}_Rv;I)92bWTNYY@{8B;-Fd1Vh)(_CR#noqL&z3L}nMk7Qg4( zf=d=4He{15fYCI*WqWwZGvn6ADHtK*c|y!}Zbe z*@wt3>07^95Nk@^AMrllX#l^A}!jhT6DbR8r|nZS20CO)detRi3cB{Kd;+>)ls z8Z^YO@r*j;v@D$PsbK9Q$50EJ5g=s!Mlap)DFp#E1*f++s2@^@;%U;f{!1eA1!*d~ zT&p+j9xgzj9#sxCk9KJoY<}}{xV{ObsnU0kc$-#K57_M$P}+bWRMNy`E2D&VvXZIn zWtp($-mQ{1+6$BinqH1}q~Zpd7e@f&UF@q>?3_RJt*DiyzQD#*>H9IAL*?HPV>+%O zvprSXi^{6S;PE+dc1}G8-a&HfNZzZt22Sq3_Do3NWaMc1B9KWVUQ!Y0q zD6onGWYhrc^SF&O0LXE$t>ahdd(3HN`iRBOqRC)4D(7pn<&GPNu;9Vjh}Q~Ic4)xs z9ufhs!#_b!$UL5HllAV*Qc)@%E2EASZ8-lC$}R+6y-}nL<{WK(2Qbyv-6xFH;(>WV z|J!SagYN2JdlP4YK=WLI?eIX#0?cq{BWu)`K^j&`&Fr#4Q26XXey``7-VF--Ilv}r z9NMYHiq*3W^9wJgt8xqHRi|A~K55!2{KW}!yX|XOO-u3Ojh@AOPuMFW%D}SGq!}?8 z!Ft+!Vi*i|KEUNhy@u4wcxJnM+YgY8Fujm>+1$qXUc^0R2LvDX#HT$!l!F@dk9! z-jbMKvo3!7r3fAeB;Ovw7T`V37&jlFwcLgeaav6c@OZ}XYtsA?l@U2N-1IV<@}P;m zI(LQ%LX2wwtn%^mX*9g8G4U!d&B5xhew`L=_33a;0Z{Lnm21~pzh0gn6eW4eEZ_L% zc_GzMwsCzoI7qixEkZgR2YLIU@JfP?ObpaT@O&{o)G{`6-@6ceYI%Gh;7mO#kwQ2% zwuLc_F74HOcG}If_P{Yh{t950VZ?H|xn4p6x+AV54~!Ls)%seBbT08iDT7l;jWgmn zv%d@IuZXEK9#SgFLPEm#6|g<>3#-Q6H*V?%?&MqZBJUP~o<)4nmK~zd*@81*6qrAD zm{OIpyYXd?Q>XZKy+Mb9{KpRJ`EOKL*fqdBnW#R;pfCl`xOkPL5oA}le+Yonn>~CYrQVZuq49<>g0*= zUUeXDQ6d!es!WMq?wa;{uzng-8_DQPHTH%i)yDw`E`0JGa=SKGHB`3y-q%T;yWGl2 zLHVjnLb6MbS*HlEA24zoTC@}&99Xhm19)YE(!4Q{@2hgWl55|CN+4QQ$rb+m;{xblGDw4 z!)Z~fcJGbbkGL+{?*eoVo;9^9IoPB9`+2RV&Kv2q@lkA0Q-QcIEf}eVrgW}Jk~K~1 z`t?i6%O>x&?dp%%O$|x;`!zH|X&uCZ5IT|iW)ZMNVuN1l<-D^g1U!`2A&L6Nn$+aJ zwEM>EM|vH=Q!5)S>2hL8!=r7FWz@Tym zy}a#cLM+~HLwiv*pU-w@?br4hU_iXzdtG};=l&Qx{nJe);@0PLy+uPE`~h9lI{z~M z0Z0j|%$gH3sOMe*D<%5knCLEiloNV*d50nW+DVs&q%`L2>hf~`$;n%F_v7KP%+jsH zQA#7S>?jS#jkZ^p5P!4P!1H~MNrPZ%7bX3{<4eFudMsGShj!1vG zl+77H9iKA|E-1LAq!`VrCA+mYf@&4gF4TU~q+3cv;!y~9$mACwEf0ohhj{DNY$JBq zJ**msr5C1$d%zyaO^)->qM0)x3(`#merg~>seI}u$vpHWUMk4Hcyj_oKWqsOfa4Sw z1$q(*m*rqZPr_p@+t3qGcB3Y?RJyB%wuKr2TwF=hZ0S!@jP)F9%_D#t|A-~8X!A?d zJVoQ^zf=$h!aAVuM)JxHwVQTqtbPlU8`9D%iAwHaQjpvf`u?<$ltFIQv>*ui^~V`A z&o;W1$={AEa-7O+7litfb3pdk4axKZF3f2S>4}A8RFVjDkf44!-He53NVMy`aesSZ zUcDQ*a-Z7mI)+mQ%K#3|jUaaBZ&fjS+mgSG{4321#&$4u{_U&jX%Y7%WtSZ0m&-z4 z8J~swQj`PC#et&?%sK6hWG_b=2IrBwr#@BT$M~BjM!x1$Dm{7)`%tD~H8VkA`F?-F zrQ|p}s0x)8?e;vT6u)%63=G!i0N7hra%@~0?cYRBD+@t^NSDbhxf1)%w2(9%o?8B$ z-3{YZl7o*~7h^}E4&$dc9-Ah_a%MNRI?ap(zoiwg66Fi%vgIqoYO(*eC&SoMl;ZcU zMfvj)IDqCvHHs9&kx+A77|Vw(J`-v^;SY*zc=27=d?cL+h@)r7X6g#2oXt!mD zdM3`40T_$9Y?Cypo&6D$qh50p#7eL{x^F=gWlH|dSKE1+0F64eP1z1PVC^Ctl?Puh zgHc1)U29UHs_pPx6L!MSp#R+$PGu?G9;QDee~Yi>m8G6~o<(Gq_7g80>}foz;BGe6 zyMntA<&qsR0dCickF(r?ri`-^PSf_&l-ZYSJ10O3J_GyCL%zR1S@mU{ z2-nEGbKlad@XbYj0C2QZebe*WlQ7S;54^u>%M?j#2a%PDq-fDY5jQ=p)i^#5Yu>>k zpuhQB5F|%Z8L*+`xaYnj_p>xWIs)~}^5szBboS7vlmsuMM-W)9@Y00iU0aky)y1>V zC##q?gur?EfeRtRmGd&U12s-VpgJf1Mqh1g6jjyN{;iVaUq7-8Ev&{v3+u(NzoW

pC5e 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

}JttND>6zKdnEg@d`!Vc!AQ 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≀jML<0S4=%RY1obL^CLB3UqA$;w+m0B4q-K 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%0DDRw^af@HY1bLabbEfA_=F8 zT3lEl?@CeKe8p;D?oEDxM}U5fhvGuY=39b?)fSN|dj{gKqs|MsLUt2u-^`c`OF$lU zhfW0rMoRhOSVj@!s%7^>`gTdtKx!vPeBX--Ojxnxc}ydL zl5@khwJY+APC&2ITLh?seGwjy zZC-6HRtS^~nixqhqZmXzv{GJ@WtFXGM>V5Kfh8m44XEzV<_YY@$DHmEHdEZ(p zc`(qZfGD-_jXWX&>-`|$xKtIM>>TTIJ=6rGQR?CffWM8d%5VO>a^v=A75vTHP1cwS zoo;#XLZ8bJzR?^Lvg|rTmz5uBgiOWHUSxAxsCkDIhSju=aP;jHZqzBav?*FJpTBXx zQ)Tw{1S-QHX-FT`l7tx##ncC{Pby_IN+({Xz}%2uvc!6?%@X_i`T#fhPk7dgtkp*G zhBgKFa@}Pr(a*x3(QgcV&I)WJdgX^@a&r<~z7eo9-pneaqF8#2GtV z2NY+ODeTM3lL-$o5(|2GZ3d4>T!&uyq9#r1n$E{CvS~0D|Ui4>>AXXL%`J&^zKwKtXRm z^(1;E=0vp3XcEQ4Q+>vgmfJN;BwtR9p>NgD*=oH#QH@2L9cDGw%TL6*l#x*lCX1A2 zvX9w;`OoG9F@Z27Zq6QN91~ddqZISQ$7Oi9JnKB%y#G{MD10DElj1(qWX&g%29_UT zg(Zqw`g+T|t>uv0&-G=IYN{cHFpUi}$M*9aVH&gGNoeE5feTKB_)o)SBe?I5gt!M%3 zlCBV^wD-W`8`bB1hh0I#utHdiOJz%P(Q193vRL@IvWcgoMaGyLr5XQ42t=uFF5)V`*w& z%zXOrQ&N!?u!ExM{LK;l)GKe)`Gk89awcXE7>KvK-I7%|mK=EFxBQq-SbYztm8#sh z;pDjqMX}FIE?hYZoFX}y@%*<%q%WhN)s+FlHiY$Vj2cU_>jrfz;_5JhM-~&|kekJ)q zpcCi?oYyzIN-wH)8ra2ZM-s)A#JQH{+CARf0d*Db^X*vsGO>DZXxVThN!)%T$^BHN zn6$d@p@GG`&9LO&%-%!@244_VD#xKAxM;S6lBI7gdSms@)cbbS>!3`T*j7?q)%gdj zjR9w^ADUo2+DPo^vXHjjmBd`0AE~z=Vcws>6LXA;Lr6^G9%dDw`R_UnhvuDr>ftdT zj<>5G0{7m0Wxh zRu|%Kc$1J}i@hnh-#&9+43r?XpavQec|itD(B!pLYSNb!iWeYuJtw{l)PD zWSh!lB3p#$eIKnZH}y`D?84q2lII*$Eg2c7+GBGNwMJYNk%JBNM|BlAD z0-SSCR?lthSd&YP!gV*Qcg2{Mx%7T;xCrbAiOIzY$F=KqpCr%XCw98aH6iCd16D>{J26REHF^%-nS>^iwd0fTFWZ%4jh6hwMrH#-rl5AiHO`M0nYO+li8q+3f7Eta|<28rS?) zXwfL%NwW$WyP8x7uG>s-u3;f-aqBEyfP+t!Z^_EaSh#CG(F0V|Vd=IY&9Or>H@Nx~ zySwNM@2m+5oMK>}n=O1g3vqQtJH|H-VRztJ#XSV;h>YW8p_+ZQwx$OS5_FbuGp->i zQ^u!R%{pPzLD9d0wdg&|tuCX}Wy$cjOAKtT8z?%)h3v2VZW3txn}8zfCOE1JJHH%G zMaGwAAm;j|cqfLE*}AIMN>t+Dx)|k-f^sW$$jOsa05Oa1_k$ z;^~z>pGrh+ntShUKNr@~sPJ*_e2lX9l6=9~HkGwgEA9@UkHHaBd)QkITB}rT2>cdC zz-50Gy&P9kR5gzf>dA$G<9u8ICj;wHZ!p1t(E`x*XaJS}rEA=W2o-Om&dBxdXza3D znkaX>=~0jrIMgn(bI;m=d-g*`zh`R3flMxf(lQBjko9u3h%-ezQ3>!W4>g&~7&AHM zswF`Dp}*q2UITMe6zi9xS)L*MRE5LE)VoX)tf~|u59!eELrJ;TlC`A5s)$!aK{BC)Jl@8Z&dSU=|F$_PGx4)mv#LPOlM0f+;YfRAW%OPRQ=UNn``Ig|o z9=V=zqG*%PIgAl_na+wHIohx_%o@PA79sYyanr<+{tY7+2yync_8t}>J26Bdpq@iJ z{nmVU4bCM>jxHniS7uwcntC*T01*8g7|FXilZMJ)YOeFru>IiWsy>&@z+wxOJ08s# zGmg3g^aol`x>Kpc^b;$WE@15546Rr$ExuT9e{N~{0vC#*y0j(tgZJ$${V_l9vB9?~ zfkJc-{X;~Emo_t?e*J`(0nmWg3@FIS@oi>juYdhoYm`FafkL6ib+%?U`)f(OA_si7 z%-ZCMEXhU;wK!wv@b%wNua*9(8}h__xxhQI)e#ht~YSqc(oWqxR^_HE$f*#vg2b z+D>@^3UBZ~gKSJv%-NwnUiwYnR}{Xr^3>%Qny1`DZm zuNtR-K8Sh2y~hmiR3`@(CttMabFq5kpr=TnR0o@`9EftbVbWnCiZd7i&EB}BIokPr z{f*wzk^w{{`sSy&Q`5@QbwA!P^!f`h{7cQB@Ko|Ua4z%+ko*S*IH}ZBr;>2v*C9`S}d7LQyOhG6un_w>!5;8Mz zAtF`UxO`=G>(_`|!;&96ne|1c26SXhTqB!KiU5dK(W9|H@S_K-88d8Gg}vCAqN=6$ z4aT^g31eilcU+?9wg9g%!$57~(?Xu#!xg{Z2Lv@}m$7?P3yI}QbY36kYlS2cGgtxd zBNlM5?te%;C|~~strUhccCM(n!qo6Tz;2F=8FbNxZel=&QSUhSKV_I) zCIp1K$x16bhPlZvfGCi(pEgo}Y*37>!?O;SWXaJ=q=Xu&J18OzM}}=rGu)k|PyrML z3=nf)oSA7P=2dH@3jwEmt2CmsXCXy!;K$Ixt6o4jm6k&OVDg<;Uh@H{v&tX=*x$2y zcU7yeiZ*%W7Jf;%K>ptl z8@qwIpTj+D>*P3(4+FlL z2m^EYKQ@|IJJh`0;s4RB+8w`v_NUh-dbd`(*{x_!ldSg=4C^xBXO6u1loWAl=cQBU zA3nJU`xIY?yPSI_YdM$ez@-6)Eg4Jk6(`NY0FPPG)n`Y zNi}n-B{h|g$AAB;;n~nFnm8fOcfg%Dw96d){uQlomPI6FCU+1V?sqih*g_9WTJ|rC zF_$0Rw-H!Rsub|FE$1$Lf9By#t?SnSwkto;aOV5398UpSVr#L2-1Z=M*5}85-WPMJ z-OcRZHVAZG|FXf2{J=^Q|EzQgSm}JZKr%3#eoLxAlEjv*nB+Lf{Ip93tSr_g_BI+wCPY z10;MpO&k1}tLZUT+e)|w%!3oyD1YetM}NA1fJ(3}0LscfgAJxSlm}v7wHZS~(I*L=-t_jCi@AUQ1pw2MlaqS__*|`k&-GZ{Md2Bs;IFI1ZvPj40rq=r_d?3WVw%x( zMO9h=z-Heb|HKN|--P7#Am%evfW44!J5zyKv2L5&MgNr|0hhl~MDkaP9Oms5swJI3 z+?Qc)Ac+@%fwL5B(*vd+-^%6ucbbHLqlwwCG%*1lkS}Rx$=uauU{|{~xwMWkT5sF7 z{rj#gzwJtD-L3#W*$H}^_CM@Ogi-Me$bVq*FLq|^z3ly2kVY9l_A zB?|x!Tk?my|1P;M-z2x@SIJG*x^8RVl=2K<$k4F*JJ>XQVghHrxjlF9ui0Bw|T)8zi$ z?;4}|1t2qzJ&!8v07mN?g8yBv?tYUi^Izr4Xm^o}Y{RNw>HTn7rg_*9+OX-X4S7&z z%5&Gwo#z4gk`n-5+Q!YD4InZbRJ?tx02YSGC^Mh_>&Btd0KV&48RKF9*K7uZ=kEQZ zRe-@A?tdZP+5^JJ;;jFjtAD*ljqQwUJoW1}7I6S}5g#vej48k2z`?eKm5g}6&CFDt48YP07Vn?TUieYiDK#BcBe33c)ZAwj0HJ_(55=GrcAH1O`w-4Ldv{kkVH zLB^g~*6nHQnPc|A>s0{n{Z~!D36^}@r4Yc}N8d`?@ykNYXBuo`SdHfORwE2|!(#i; zp6^sDJ`8Ms@72eRY`}W0_y6^X(1YKOD7fy30IK+R6%1DSSKSL}*XB9kHmz!IA7t`J z?nM9qiB3#mdJUsuz$HzYN$z7>-rtO2*FIn)VmejKKcG)u1#I2Rg`RDaH|{fqK?OKA zS<8##kNYqN_Mu*qa*#=d#LK(kOKS62)Z?__-OO)kfwp|9!N0ZZ8BjQJ;t?dz9BK@tLec9soJ~=s3&V=33r!yeo4S z03i5l*gDyN6;$A`>6*Zq=ys`%kG|7~mt}i!nvB2$qnS>6==a|>vogIn9;w_T%qwE< z;)hkB0@9Io=;03LAJq5pJ_F2hnwkEN?>w=@F!@B{#u0;G82!fzo0bfK53J#P?*Gm1 zp{x7=D})gGK7Iec81RUxT5Wlty3TycuRmP91W@|CzX;+!zs430oz2p z|6j~~XH-*LwC<6EiWLz7m8PJgRFU2iEQlgSq)As%kS@||1UsSF5T!*?id5;ni2)TM z0z&8@E%X4PgcS1DRyOBY&X0G;yJNgRju>{e~a4y19Fdhzh`Y6%Jo+Io4(gH9O1Go&sb0HopaJc*x&IqJTy#MZJbyek%tW0CA>8&l^iC=b-}KL#Z>i0;>c5 z@wqzPC#I{ zw|6Z}m2*$PKIn)gef(|q@L=xXR^bB&cfk9=y5TcWeUv+olO#Fd?|L^vw|rn^ItJxMNh7m=ep#cfdoxSTYOqcm!p^jX>@R+}h@K<0DbQ!wyWMKB8Dh3GGM0f#4 z*~O5Gv5Bkx!GDhM6tEZ5{^n=!X&Ep*3BtR08)^^QCBCw(-v;`U4MwQDr3h1{TWICTNOCaFJjY}mtZP2I z3p5zz8$7Er1b!GM0v~O#R>IS-w_qY*2hd38)Bt>dFQ{OtCC>FB@LK;l(v=Bag=qg@ zT6OonZSBu?S)r6WwQkB9!t@Z>@txW3^ep^c)0(`q@Sk+eTnwp3XSf>P!iS;7a^e7> z><#i-@RY9rz9c@7vXlKkcj`1aVC}K7XqfiI!T+zP-))3Z3p;Nb&!q7K%jY=3dz%9DY= z(A01HJaP<7THH+fIDD4Ud$$!m+a+ZU90CWXrD1&XL>G)3l6pz=*BQWTQWXMgQF@c= z3=^6+IN`b+&XX{yUPXhonC^6k{}0a?-Ygckhu{G&v8ONLQ|s6RbVBZJ?nhTh;^0m2 zz%Ik8o&UWXkWkfd#KX#(CI;+hvkmbRyc=PF7kSTQ!0N4mR47D0QFx5 zDM9tg#jq}}840N0?PJM*UZKMf5^vs5EQVkEhU-HLBAh-m@zt_}1Ex6U4zULe6RvQV zyPdx&lDX7J6!>+k8?^dC6Ub#!?re$t%I07A#4NB$%5_+i1CylLbsOFuUZ9Ci9ez8t z`a<{*@U)r$FNkzHz4;zYev+$psV>P&ys*?y)c&sV8u-O2dK$r7yE+;8Ln1&U-joS* zn3fN>Fg?7_<`-w@j)zy`b_x*D@wdhQDakb;%+`@0c^D?5&b9h}PD*dK!b@lt0=9eq zg$&K#*wzYRYlefZnJX+2hPS4eS>G>C+#3(SGuIf*q5j3{VZzg2-5U*{D49|AA5n$p zV7dZo3U2EO!85N&Cjyj&gbYexpd*1A4(! z@pxjaXODtd*<4fQAK&+d1KbAlEX{SNVb+dTy=(A_uWVoC9KcFCjNLnrdNcER z^#zK4RbtRcNphv%whZlRlMWpWN71Yl`|k-hEtW5qN!v8UoL0}OWZLxFv7lR)_gdu| z+Xw^0=W|_0_)F;voy`f67ExOyhoyWMCW`M;2OsMs>S-(NS7OX(qWpd8?Je{?O@+4a zIXurK*W+bXOq;FC*+eWH2(z8Ra$zV(3k6`FwsI{FJ3`m_B=H@*H?}})jpOv-TBRvV zKvVQYp9yU!Co`8amYUS*R3fIfcECQP=s>+(uV1&qbJ-9|JX%~sY2xj1pZ@zoyT=?` z(~L*ov+HQ`lPlNnw!=?+^qt632fgOivxhCo@3^uzeyv~Nm@H?|iAj?My@+vvp1(7F zyT`|-rk1Hh9SQG`XR=9Fq8Q58q%o&)05cOU`oKx~P~Hn69-5POG` za(*08pe;LCfUFM}%=0yi0H4&;o(h6j*Z`q~218u;{7 zDvMn?8Tst7`^d@;sjav)EHp*fye5zygFEK8c0UF>qz>G16J&l#UAr%u_v z3YCq@wlBE;(z?c+)n@%&imz%p-=_@1Yhs{lt5Vce24( z$2L&8ZD;taSFda@U*4xS)CqbiNz2PW10BDjrcA4cnYC%{>7x_9UD15L6imJQ7oqg0 zW|ymS=Zht@_;S6%f4 z9TcV6aWvIXNY|J$z|Zg+`rGoX>AZ5@pe?OZzt?}lil2(BdZZkka;>9yJR>@zWU6?BP1)e1Gl|IMnQ&Wh&Ds@oIRZju zT2Fy%QWH1zb$VvnzL@HRK4f{Nw&%9DSYmsZ*W;Nzs!R!3FCwm6Bmp+g z&CcoP^jV}KE(IeN8m{?^ef+Qt;0}5N6ThULi#K*vd6N22Fnz&D~oJMp;qh<0jV6uVmYwlo?dx zG`<{vpndrbOOm;3z0BwqXRqql176=YSw7?Q8F>j>l$I@YjNajP+F6iE!M59AVteRQ zo<>BcQFpR)$z;AUKeuzKOYuZ@T{gkgG0#Ae5aT*FMkS&czQv=7(b@A}l+mPSaFc0s zb(YIVHdU&xzqp;_t-T{qwv9WfQza5u^f%4 zh2p#f=nDlES}v0;81=tsYbCsn`b@cEqRUX0)DC>UUufw~`^+*~teN0kd6S07l@k5{ z^0yuKK7?4$M6#Jf_QwMd^ZyvFN#41Jj216Gxal2|pny9KSg*B5b5(YoxKc*Fa{wvY z!D1%Bcl3;UqY#R-pO6T+BNEE(bsbF4#=aPmD}}rEW*gX4k4t`-a_EE+WK58)JP%%bhQUWCqNq3ORDEFOebpG3bjnz0rW( zyHdbjJgWE3YcZ^AE$Nn1-NDw$;TP76j(_CmXIUGC^_&AIi-}t-C2&Z{;Yo(AJ{b+W z+h)iE;Wn65sP8z|M#@Rk8!KY*kj{m~^t(Q(h)gMQoVPh6WzVZ!(Boq-`-H50pZK(` zUrDTMz*nqX$H>BaXl_`p!n?~;)uX?HaSzkWW#M%(c+vQ?(lAf&p~eHkvTg9K`$3qh zDu8t74=UV+FP#$je_75`>lKVXR|EieQO{q%%bBj8Q$@HmBEs~srK*?3y=-v5#K(2+ zbB*drfVE5UdbC{}ovvoZ*uQ`dtw{(mbH8YzHjIT)Ho!~#a17TXbmF|ovC@{KA8Pr0 zMTaoB2EEs~nPuSQvveQyi}TEu(cL|a<;5ha26?NdVYX!S+iNc9zHuf0=~Q(NUS# zPuI)#XJ3n9ETR~4lNF3*<7xl+8jxbG&LY)=0)4L|4-`A(GjOP7*WEjJKAVg2VI2zkL`fCoy=D0tx5X z0Lo~@0irj~_goCODf6`8mtCfQ+5%5@+k4mYdg?QEv{xNizL!Fj#I?l}A@0g;*)?*u zt6zl+)17Q*VZSMp)qmu8-qwVhTNMvdHMx#=0M5H>ap@EO@UVHE__6nFUe2|gFX=39 zwVX$r3ZL)ECtCoFF_}%kBKu1Yl)DC-rn#2N4dm$BqY(#p0E{`9w(sL7mV0GG)RBsbtGlLB>jC_jv@IQwqq>&2DvZ zJ$>t=6*^kK-`h{SL5W!EU^x?hzg&Ibu4BGEd8*fb1dBuobKc}fxcm_zacaa-RGsx*K-LimOX}%4BI4L zxX0JrUNegiLUa37qcQCC_q%u8{E`=hV|&))TtT0;ZnsLaz4d5-Ut`uWzS1I~w6T^2 z=bYbeJLe=sG6p0I&S&~{x;1O%e45ggQN&ayA?c%T0JaPzy=E+7 z7^eZZHyxTUjeAdZvXJBboElE^lGKkQMo+ll%{{ds#5eSWy!C#bL;$kJ^V{|_q7Myp zalJw_aT}T=D`-Oz`RU5G?p;`LXtZ$eA&!ZGvZzdh5XsVkl0-6{iyCb_eic8qP)78Z zpo-T~H*}IiPIAm9FUK%GzuAf?pyVX9sHGPmwAL4Z&~6>5AjJ|d8=QI7L})rAq4J&3`m1iTBIGA?a0uJs&(Cdv z$jP{M6%UZwj);RKF@%xsA-POV+H;)Wfz-C>^#lQVvL$2QvV9ao8x+d<>wHgs<24ia zmabBa40=%SI(XwVRl6OT%(5p6g0WktKtIx7qudJQbFb=~CaO z2s%yPQ3a@bal)U6Bz{Mtcd5ZM(G{oS*|k{*K>fLg;ePu%7xN3s#8UfWDo_H?$i?_F zaV1Vn?080r_$J|#+m0No%2Ml75aw3jDIHVP$TMg;SB8u=Lt9ON-g`_|)2G=F2l+d{-4 z8MKM>si<7VDUhs-3I`I`#2FxkL&6&BH zG+iJAqS!U#ARMVa=AlI{`Q2!EIMSSbWbG!2XK_gTKsVwmofc(8ixT%1ggUk{`Ri^| z=HA(9m-#~bwHU#j(d!)sXhImjBb(%gBaULc`tNIOTX?rgB2Aw!?3!a~MuzIHmdk>g zT&DVF4SyHvW=SpA8ac2vguZomh&1;wuD5%mJ0r`gZzCi-t!{?OPRYk+#J_XkyBadn z5G9l9<@Juhb}sa-ol-taEI&|Ijg8piLdP)S%Co2TcX914K7ZFPyceLyh`;P&*jsKH zoxgb7zCd8$flilqB=)eV2TW+IN%Hy4LRYSTHeitg@#NElm~(%46dU6=N!7 z^GM5x1wAA@+KCEH+VUjXw- zlGrh#?nlM^v1rcg;qp&$hvEpGkOrr5jq*V-T$4(yKzST2Ki=;j?F@0y1?h4nfgR>$ zV%Z-A2Aqy1BYHlJtMmIy*FTpMvfuu4qSVAatv*a?O5VsQD?@ibxSP0>GfMGo*Q&S3 zrRne8dXqMMyI&y`;e4gbB^{8ZMJ-jjr_OurOq&snPr`6_xK(|Id#YtQ34yK^fzumTEPupd(5_3ODX#FZ%Fut;%e z!X?@?BiE@n$#^0kA*in`!OpiYZ?UBtSck85s}*(+3rReVD-t*NBGKDtWS*J|_|tEW zCm3&PU}Pd{Hs0ZPS)Lya;$}R{ke@b>K%`(Md?^N_pGp{JWhIW8+#p|WzOlu~QYCfg zuDwkx6^83ClL9$zre#T_P+K?d8E$zs5W`ZRUNjsvBR%=HdaJQomlvNwuprr}jN|WA zNV@CYYELP+BrlPo2PsG`Nx5^1DRWK$a=Xv###l-^C}-C6l56Ce07{b0Y##PjF3$ld zzDV>2&t7cuGwz52D-lE0&}+W>>%^P@&$Oh>Mpnd3Mjf0+fY7U*HQhjUZ#K4xHJDKJ zweYLRl_Hk{5OMiiUEzoQk!Ft!;?v8$X8gat=_3tCJI6r)DUHc>hC=iw`sR$)W%0R8 z`iU6&hRvwCPmQIYFH>S#X3H)kH7GR$RS9 zjoue;M|+bLXEOdIY(m6PGGi;HwfKUc&|pj9fD*B!m+q9_oW0FXpB|R3C~A>&^rjLw zucwGkq)dX%8Ku@@ zMY0(^Cu^r)7`NF%hkd4lBIHk9m}ocNpV7u9$E$X=*r(3LnLzBKWGPaAMhFVI_Z#5JN_{GHL%yK2(#VWZ3E(GdZ?ouOHqWbbe_*`|joVJ9q zU8CEyamDEZW%3CV=Y%Uw^(a5(?s-B*LRoLQ$(>y-FM1Ikv2Cbv%(6k*T&@UF0~IjK z&s*)Zk8HNtN9~|wCe6(L8g|b(5c&}r<&?49zMxGF?sm7tkc&mQD^v*}uXb>l08$zZ zxiP@BN%X;_#PUpM@3O49?_^$k#{C*S9#@rbxtAF8%HekTx-vy^VnJ29#&Y>Pi9iLk z)X5P&9Bnj{o?68$q;}RH*eE`PO#$oID6mJS#_Yl^wpoQwxa%{82EV|H3CGtUtxuev z%Btl{E#d=ujo<@cND1n`jaM8`(_0}_5i$+Hv4nH`zpI}?6jyUE2lb?zINkasU>-@; zmy!s33F^L}65r35dv@n-KNrwFZUOo*w3zDO24XBHr+a2su<$1k)fSO=e=$J+JWizY zWXp(wq49ntq%cbTfeNb{V?-UP(dm$*HNSs%fH~l)UrJi3vMGWDj_`q!Z;;g@!*%$% zP2wUzd+j6^i}G>vJZF|Apq@UDB|R-D-47x6RD~L+4G2cxdStSn5D$tr(_V~^I-9lI z`PRs*N8U;WhJfW+q#DpGc3x+6;LD#1T;nlS!WZwlOD>wB;2Gm56HER58v04wZR>a?Q@ zIPZqS0gIoV0D#N|5Cj8z%2lVtfOP4ng5DHMK65IJFD= zf=u(@us=)bBlRSiM+A5is@vZOEVp~UUQ578l+C<#ZI;;Dc4W(z@WjxH=}gs*BRJE8 z=5rtGo7Jp1Vp)}`a-)>$g3}fLeKT_}otJxW9ZdZk5TNK^5ZYuUvE)c;;PBM)vQT3E z%Iz06R;gPw4=($s6iFTZ=hFP9k{^5^J=}k>TMj8bCCT(m_xX|h(o0wU;s9eRcOle| zt4ZpcDHi}Nn&qg16Z9(vrm@XTg#6P9)kp$ zVT4N1Lr8hfw(D7nItjAHJ}!+bf8l#cHu*NDD6TT~uIyn9yRt73&I(7OPcI;vlpOFoNee2N=Ylkm|E-edorh}gUJ&(SOn8SZf+n8=N9 zLpm>Xx#B)~>SbZb%GeIktoPfo-2-uQKmp;khiExr&WF*v4%l@p6Ub9em(h#oOYsgr z3)Bk&0IA)-JOSHT=i*dpAe|UjR*+#02hY4wmA;l=*dTYykqYumeXWUD%tN_{URYL#8=dHy6F7#1%OEOqEvhr z3bTwV4z4p6+JsP7Yh$5d4L9S4_ExkZGatAEu%!ebEY?fW$zD1}Hp3-WLV$;h!ZuM4{*=<=Xl^C}3(yjlPnJ#$Pp> zUV^YD^J!KjZCYcb#OE(;%7A3KM|bX{in}0koUmqS=lDZ)zUNQPJ=(`EhWqmF?UO5? z32iT51oC_|M5^HwBzHOhU}~;f&mS{8_7*D>e0b0v*p$-hdx338+P*c-#b1^b9|x$e z!U9-9bEeRNbC3mf_h`>)P_%!y z^gh(Ud%g$KkVb*d?dYC7&>^Qdb=3^#ISxRTN5*{Ned!s2lsmOvvzX)FT)L79@37pC zxw_@{T~;b23E8W)CUW2&a_jU{kw7dDMHHY1QUgHIor@P>Q~U})VFz9moqbkF7=`^3 ztTJ-^JKU$}ugAz)xVO7~^~lsQ3Nt`0H{F&?AT4%F$IhWUo8YlA+x9x4t)DBmnf0lory$+rmHm8HYRW6Z;|HrpKCnbsfO+0r}eFW9H)n?>i7BT z8X)P%v$xKuY|OwoXEMK2TBOzd4dTN?U>l>@MkWYy>(mM*(D@90fvBmzry^~Cv^ z4%gVO%CKYyo^NGskSj^_FSBmtnv$B`_Lr+BtRPy>8Z4NRA?2$^=9rdDw?GQ=0_joXq&_L=H|H zl35GN2)!Be=8324G05pa*2}IA`IwWPYC{uVNbJI-GpgjeVC4xqja-Y=CVJH>WrND> z1&FyX$0MeFOUl`Ut?b;o8uJUou+fIWbnP8}fU9t2;y;m+ukpyOJE5u|++53vc}eaG zi>rMJo=>gOY0k!3%Ezi0(wCx<6=Jc67c9%w z)JCve7KhW3oPJ@B7a9qP5~aj>+xelVr>X^CjouVmR_Oubi|%*o5)X@%Zc zkm+4-pG}yg2_8eSeQl^o91N-$Yie@HZl@i^UIkJIj~qzGn2fW(7N@S1!aTxMpj7o9SsNU||sluIZ(^ zr+lPoM=1`B>4rO;ox|)0*A=59Dh9}x4N6H1^N0o!G7m9<#ez`P#4%XnEpfJNFx+CV1+^aZXd8gxQ7={L&A#zhMkQ5UHi;ncK8lfltv`bie{N~5nI zH>o3LA5MHZv3J>Ia;zIaQuwMD!Ijr$PJ6TMy7Yk@KHVMg&-O4{jLq0qkIT1p!+ zVw2~`t#9Ha$sXOB(p+*Y5|(D|Tsrv{p10@P#qiqU;Rb#^gPRx|h9V>Z zJ}&sj{#t(}o{I#8{tXe~*pq)9NwHg=@~k)m6mqkK639-JYRIkBpR*%VP}$7B1ZY7y zRym*n-}p4JZpO%hOXpf(y(D~cLyYr~R7%{DK$j*)=Qa~kqGd5nO+)E_IzYp7dP zyCDCrW3sJhd7&WbjCj$$abaP534s4x-MM=&W+7Pax=pVM2P-Ypp@*}u#tE}Wr* z<$T#r%i-vCCSVwu(G~}aJ!s2sfzd4q`2GcyhJgF)3=m$8h0OzZZm|RrTD}UUWGH9o z-jvw=T?K4E-1zK33YllJGq1JLNYFIoPyph#@A9s6rwSnT6EpoM?c1kV8q{x;``FKY z*8)K;O&YD!2IHS?>rgsV)4pX*p3|jZmm010kjHKQN!d?~Z4J`^aQN1`c)>wjBgTQ` zg18Z`7^Rz6uyf%vl??gxET)6F1|^yq%S48Ci!uR&THev(uY;t|iB3S0#;Cz| z_%uxo#f*)AG=bR-Trjo*G*}pPOsev60&pbN$ny(db^9b?~Bdnz@5yMZeZx zx>`yptOV1U#Gp1VdO+Xb{2y2g=FS7Oe9v=fC=y3Ok9E#G$EDha2%_( zStPUc`W4GtW3Nm+^;1K+g9V-iv>MwaNcfMQIrNSOg%XJnI58)ZA%ojz4@clpJ`^Kx zicjtU)>rCS`g)#gs_O4zu^S0&Jz*&eGsgF2&^Uj6%kbz^vtvZ;r$Na+;D@Ffb4;<= z_j+_X+JZgNlve83g3ALpFU@q{CDsI( zBNhtMnyi^$p-uH&>P5Z{Q-wAnYG*E~N+2uRYD?c$dPjS!)X2-VUAUPxOD%1P#m}(Z zKFKla;Q$4^WcT^Z8EpR0I#zNXE1eQ^@~l*Ta$M=VjUu0i;uhHqVg$Y7)?Wo~IeDRX zx7iM+<*9v`PPL@l?Jw+U0N#oDfw6J*tMWlJ5cKjmm^MpjA;?mSTRd}P_gm57a1Egbd(Cu6zFr*9kYAH2%HX4<{gScsUCP znp;$0!mP`#ULhzN7(xct-6ET*Uz6`mp%%`55^3fJmOt9HPQC%(J?~nv#N;viS}j)Z zX$5hfK$^-zn^lwK300&+f8srDhtRMRa#@EamhkL2uyN_4mC0t^Q;_<|xhyu7X%JEB zReJRSQ6j@V9V3>W<_^k{?6mabFP~3$PWM|NNg5$*ctFzugJ1BR9f0W*-v`iS0l@b3o!pT;gm_5iW6_Ev!}01o_dDa{jE0n-tHF5SCz2u< zd>U@KE@Wp(Tn1uVf?Fr&WmfX+Zi6(>9&dt=gaVF)_#OilHSj*Ukp3vL^zu8|mX@Gu z6bKIc+z`Go9SWkUy?q*Tt|z_A zzqw>u)%Ta_`Zphs*+DG2))_#Z6yZpym04ayKU<^%wKPq@Z+?blUi}J*rNrYBnp?78 z>Y)){%t86ond%xUh3aom^URqoH zC5DCXqzwLSXI87LyW+<%uZp`U{TN(oxeZ`7pIXc-_}j92#FwbFlSD2bXS) zd$(O^dTTkE?>@IdgZw~9(bifUX{Qsx+~?z&3gN^LI64+&kSRY8TlK*AY`^zT17w7> zMn$dU>VB#HVcg;>OYW0bR*t!I=T1{!--Te+1jjLEMP9!Q&jTW2q|)5k32ZZmr*cf( zGZIcC2eNWGzMb9FyouA>shHR()I5*!a}cE|n;2mDKKZ)b+byntXfzW_?J>{kjl)V9MmfU*wf|rx;YJyO%baglPzHj#lyYSZKGKv(cAA znkKrYL*YX6kFbU3 zPE@$#&E*^c3f*Gx)^V_-VgKO>peE_PnKb$B-J?**E5g?ZMyHy2s)H9n%%Ms5JiJr9 zAYEf>o5SPojR?8*p#4Wov>`LgXDDvF;J(Ul16n}Ybyx!#Ay{K~4E#_7GM)0XBH_3L zcQBbD6VMQ1xD~WZP>@S!+4js7fCDFXU-+G?!4!_Zf3*--W8hs(dk1{|Ug*e_6ROK$39|6hpMU7YCdI`T|Z(M=g zH`I;0_+e9*3e!XMeFg~f9(cpGvO$%(P>?rudk8lO(U%V^w)`nQZe`C_7}d6&U~UVt z@LT3U#)T^?y`X(X&s7nHAQYCv&&f#zB;dL>!|7k}&wu=o4yAo%ia7WDSfOgghQT6` zV;NxT4JQ-&817-LmH2Zq|J+1ZoxCL2Z$q_}!-J>uFL%M0OmHmp;@M%XmDrMBQYKYE z!h%`G{{P#VA~$}|734SSiCJ-w#X;Ff_-v=^U)PRiB{7UMjz;)NpApZ#Q0&I=eB$@y z&Ps60zvlLRGp+!l7H_uOxd}cQ5}0tToX1-DRGxs?RGkg3-CPPYHU)`LfN(L+^O6RC z=e3Brtb&Z-pkGb~-h{80d%S&SeO~RuCOYKAqcm3j_i5A`l_FAlWH$; zXD?1RMX%(?d`V)Gs$n{UTNv%qf^)UL=zUKdlUG!XI(hPB6KGhtd-rY);1aUEaN$ML zu>FyLuF21X|Fs4*)P@1SDvIR*Rn`s|$A2ZE(@$P1|IbYhhdhU5 zrf5F+^6mMa+97ol^oFw=vb6JNIhHoq>|96m=UNRZVSNLl~q6ga>LhI6@a-h~?mOkpwo zIotnu3Ohhbm6TOY2yn8i&a4DK?fFssp1Uiw{a|yM7+ed0sT-t3 zDXXN;XVl9^$8B28C|lW9cx<kS1_xI_@`79J6NgKi@cZj`~ICF2~X<#f&;&^ zq0a(87qU3A@v;G?_J6;!$VyF{H~p^dod48ost3d>CxSOo0;N@GxiA)6Js;?UY6X<{ zW}WbJy{27j1!&4vqI+IJRf1|)OSNqIo;Us~7`LTA?;0rTvB@h6=4d~$s~6#bIVt6| z{al12sq_`s*t9N!lkz!@F*((&cQbkUZ3#Xs%mDxc#2KEa2-PEh}+Y~dGBa`1s~ zCJvy*mB5SPKeC7scJNGtXztH7SyDkR))n(zs}+Z&A!NAce6%(f=+*n$>8EwU#Q0?Z zlIq46elA}m8~6eH9t9N|*|l0|L>dbH+GmCaMnF^MuQ7k*8van$|MT1bd$m-)!?5i1 zkQw;oqjr2iRiSGe8yls6EAyeXkk)<~y4a)ga8~Xw=K?v$#2b5nuYJL13|4v1dVtN% z&U`Qf-}N!T=Q7w+>XSeXvt9b0e?AdNXf=qY{d^*zvKq)&{C`tf%@CSZMu-bMtKDE$ zPTSJ7k~aN!Hs}Zx7uq2D_Lo^fbYjM_nicjxKUwyBW}uI9yCK#KCN42R;srPG$AF^2 zIV}w{e)zP$Kc&hKEZ3?3a-}M{lq&z?PP_;>;L%9y;8k}S^NLr3IG2| zcR%VuTru1E59xgu%qHWGfT_rR{Tt?#KQR}|QE;O`B;F1Bt*6`uVCx| zC>uI1A}$3aQN+o$yETtKf1SDzQJKJHDN5@y5^?&?5VVF#Z$FSQyMC#P35l!E&m;vw zJ9Nh@A*)Gga1pg=5MB2YL=PGGEVnXfI>q!>Q&XP2Vdi1Z^H*97n#D_|qT**sRhXsE zT|#YeQK^5k6wp)(lp>qaYCAveeQft?$siSIE_!vgu*w(s1ek>g=8z-Os(g9gA&5Fr zPMy>IYCt_!NllY@+P?31vP^{3HL2;JNhJ+fa3bBWtTMtc5c<4%!BNp1)WLmFeCRg# z@1dtvdtj#fk3{-&;l=|%!EQHv{|}P}V5)$n?XoKbG^9lmpy491Fa9vxkII~feyO+! zD&IvxpyfZ|&%sbIr0vsps(%;~QnaMdz(V)$VxI>e82I!3&+g!Bai|pBquWO`f4hu< z8IXqZIeWuT#rCo{{7}8Y9y?zAhg#CWlyk19=Yf)Y^*ZC%!3B@BR|)yOX55Q{?}#w& z=OYVQE$9V`omj!eP6F3~f}Z33x>&-x854PMdZ3Er8OX3xykBz9@6+Xj?1-N)0d3%? z+JN8d%Y=yhGwJ~_gJ@Ozq|Y7*yuZAhuW2<=lLch)K*}yDSQ07#-Q!<&v9lTgL5vzd1@X2<<0rOJMqY(! zfCR%gh(ULDCXw{g>y&qrwfZL7E!6nyRkA9eUhJQd=2u{K$Ovq@V-D(js(xJyzYPtF zY)r*tkLa&#M&%E@<~G%)zt(Jg%WK(LE`n!|i#lZvL3P`j6z&6{kBgb4@QqHq<; zNTFRL+p_smL}nTfk{IKfO|TC$x4#Kuv&AUHx9$_AONr6v0!o`+Oz$Jwk6w9tx-Nvy zMc^t>0x4PDAgOiK93(8#E!*E`iJ+G1atF_yNmz8M@b?&#F*1)3FByC^vBZrxl_+x` z2+bjI#dum2&rrs52ra1hF})xUvfXc>4HQ6#m;!;tZtWn^8d%Zv{C$Spm4a}MwG=CD z$=)_T@4<(2cMBIP2xp7hDlmiNkBQ++$Yt_?@ol13L0f#24o`Sq$zVJ>dthlPk1u?Q zg3sv&+}bz2dbR4-zggFpAix|pc|Dfq@QY)v-Md1ps;rQ?urEGp`wFFDz=dnhnKI}1 z3mAEs+o_H?veg-?)$zB|=2ZTa`X@Fae$)`j!ku2l}Z0!bhfIHlK_<>#9Oy*BuX5zJ{A&MJlK2? z{b)-~w0M47A<3@n1HmxHIh_>eGy5@$J~T;^w=!W~US=j7crIgihx& z88x+`OPzjQ*rj1!%Vo6XYdPn)N0*QIhDde+-_`d^Yhg(n;7?)?MossQU?&QMaPi`UfcO)DxMX8o2 zI3neh&%A+=neJ%0kC@2%nz;A00D7urXtdFM9-2VFjr(JU2)=Mg>A#gZprM(dFvci* z8*Rww1xmE6V3EKcc3>=AZ2cMnZyT^z#>HVJ?szIG1o`~^23sMItFBrq6gcSHxA(68 zJ1pC}<&pEro((Yy?T4JroXh4ug6vqNiW*!)s>|n@F@h)jKCD@P*}wf^)YIhrL(~ zu=+O9wNEiQ<8m2Yw)1kWU@k2iaJZ5h0kr73H}Skm@eGsBL%kG3pT1>k&T+_*uQ;09 zESz+EQPPSd6K&c^ZBUvzB^mCQx)4IoO2jM;3W?wkQb6N@>wLe%I~L7w)fwV=q78GI zH-LpbW=1)%Ra^f@MMM)UobjZ}1-ZF!{T$ULu{=kmstAx+?^ZUH^oE$IgsBTQt;5wN zq=n;Fh1Wz@#I%$9K}Bby&HbF!)h?xqd(67k-OtJupzUzj7uVe@mh#YygAdlfLQ@5D zO93l-6l>9iG4d3*KvvYCBTdD5J>P9u+J7$ZG*2=j$?!&Dn`iDv0w+$WL)M$xS!$6AxQR|xD{#u#V&I>m+A_;N+9t)+#O>-yKboY17&TuSp9IwOD zy;0Z~*ZBN0w%V&33{@cMZdXE<9_N7=AyiZffn$BJf9<$W50_fqRraIvV~*dlG@2Sc z%ZFML2i)gnP$1@WbdDGxhm`RRSsLo(u;NHaz1gyqMa$}0G!7_NhnMqjCOO7+aJlRX zJv~N20-z1=4GOS%dSpxBhj*W26r=!7squ+|!O$LR1&L){72%D))K~Uvj^{YNycCi)Z?CaVWghR%WVr_> zG&G52(ZUzhmI+_%j@+_0WtUE_!TKA>((0`;sef}-7khij6LX5geH?@Rp-6e#0+2LL zyxwKy;^a*(EX>?V6Pv=->TcdhutEU|m*@!6al71y=LIS}W=b#hW+5xAXJW<(uoOw@ zQ5hZY=Sj^pR+hCDdd!oy=r4PkYb$Ir`?p9x90)F^n2Ia%`zO`a>_<{ve+`QE5 z4t!h(ft2TxNu2UlS%dcoN$gRzn~`X+U+X5c`;7ZlZ%BMY6*oL7X~sEC_dysDr@2Fx z1B#EYK__MEl|?j2*acY(ed#?Z$IF+xf=OcQP-rU2We4>`ZO_Crf=oFrs)fK|$wi0_ zPxA7!z@bU^UC|_rQRn?IJQWe0Tn4k$f72F=rs!FkZ{2;|_1BQ$@gz0|uJt=&@jaTr zlStVGcU$=3Gz>fBtNZ<2-uyThWgau!Jl@QA(rx|R@D(O3)5Cab&oYbw$ZxHu)g)B44guD ziN{I!6m~)d{KBk0S$w}67r@=%Wb`YuuE)F5$F@ykL6r+dPz5I^kxhBIQd=pZKELgf znGv9_USr3<`P%-raK=JG z^F(`2&J;hT8PgQNz?4yMgvXmu<(K#6ZhLf=X+!eCwry|Y&s)6jDdS{j)>S>Kw&Un) z77Z@uhbad=RD)l{TXY(T;w{+9gar<|=@(y{?}XRp>n%G_jPq8Zba>M#9a`u)gsYUS|2EatCOZFoI!I({ zU^p4~i1mBZK_u^+kLTgeK{qPaZrlipr%w>~U-E#zR2>9ukOT{Y2@gGzW#g>waEmml zrjU>^o!|Pd4czV=-u&12GT^m_tr^CG=^eR;^c~x$#y7K$=YRRj^|kEFHGS!(`5&a3 z6Hmv6Y%U@P5m{+`6(J;B79CP#{q3d|6x&7g#cH^AO&%HG>%{wWC zK|P%<>ndUi8?-+tKbLWid;L1mBzi)!+&uTg#)JGVukV|A&0w?bT7-99Ows5R%k9X! zgRw*naTw>jy=lqxh*i8kwM00R^H@8(xD6jK5rcn~iH?q(Lg%XaVAK@(YX98jrjl^JN5nvlfcG%KK`~P}pSW`fyKfzt%Oy-9D*iJm+*3<==W| zJB}MJfqDT$A5Mm*)$^A#$~AW?8OGqpH!mv|blMMY-l%jFr+C4ZP@r8*y-QMVn0&d{ znt-5sk9g+=&%M=s6@2rN_0K-rG-;jp;~A-{0O757mRrHz3v@ z@(O|%v*%x{R(-zh+VW!O5y@{yj+_Qwt;;W;9e|g&ZT*^h&?znK?|sKte-f|_ba!$# zta1i(1**lzer)z!5@sTj4Hv&Uhb3%e( z8+G2u^5Z8Ck+dwCd!2>0&3)b$aY!Utxk-r2%rBWYx%nM6oOF*;le@1dQ5oT)x3Gr`CCMpQWKkkD_)fFWR{- z1NuUY5GM$weBD!}0R)#u<#6c>{PoXx(0|&sznIp5Zdg5C8$g+Kmn-^kcdp8Cz!vC* z!k;(5ZL_W&Qk`z!Zx+%`11Tvp*GDWGpq6wb?i+Qh9lQR;kEZoOF%!AcUk1$teXbV9 zKOL5UU$aL8yj(X3J9;e$=Mr>zrzHyrTfrVVya9gTUkomg{(a0%XQtb^jZUt1NTUgZ zd1Z=Jm%?ukJP0PHzJW8TdIO?%!=sCSd%*)-b(QTsS7)BqI(+F1dxy0 z{}-4i=OeJQ86m;&U&sc)(-&aAYxp+&%?WT+3L0$99*aXe<{JH~y@B=^1 z6Y#hN>x;k56Ud1Ij*AF*9;zXLAgq|(+GG22&d3)podf495qbOcpSbJXFM8J_|> zTX;kmUTL3tU{Da#%l;gGqdN+#KNANVQlg4J&IJfS^)gE@L;&^Y4zvA{0M0^0h1U3m z01jR-@Rvn;EV*3&hgwzlD6XO675%?D7Y-2!XDlq@28V|=w6()7U%q@oZjZY9zPANI zxN7zAr(eJ7dtVyUH_rBQIpI36hdG|@YFF-?uCHY!4&B8Dcee8E)H3>~AKyqDwjd+_E#gW2bP+m_4C%pYHCm9bhpxkZy?vkt~-E3JGsBg+}5_|M5>g zLcqPLLA=uoqsMuQ>oBe*PG~iamk52`J0iWEYM*?3eB1bL>b=jWL zoR~VFuB=5;f5Iwl3MH{0-3iua5cqcBecf%Mp`5M+EVGZi;SFF+@h5;=*#G(rpFK4x z-kT&)0tzva{r3-(=z%XA77RoRK11zHo)BjLy5~QH=P$M=1zdt9(I{ImeI95f#pWOV z>TrEVM>998YI1M%el_9=z`$0KIxfDbosS7c|M-Hy!_9%q5Q_xaZ~tNuY*HUAYjRJm z%6wh`HhPb^aCzf#_Pn({}Rf0=xh|7aVTi5OYv`S zuKMN8^M85s;KQ+RE^W>={&2P6Q)0N{?EDN|i4LYeO?&uX9(e88mmp*2`*V=d0{-%U z3WneI?}r#jw9>h$FOM_+<#F=BRf;>0G@_}#?(YaO@F|s@uRP-!bmr~Cj}G&Lu}cX9 zx#V2C43+1`$B6siF8iz@v-)eF{Jx(lH-U&3bcoA87qFw+lz;P_dqH39sPC8M%R5ZQ zaGtdxPgU$B6=#&7l56-!*+ZDHR}^aLZpOSL`p(yf4*&BBmKU-vhTJ z@w7gwOJ70E)-Hd~*1mtQQy%8qgHcKiT zSiTgJz@AZ{R3mBT{^cvb{%5T>C-HGde|_Aa*REYl0uEG~9}ZM|z)&jh2Ub%OgE#G= zfAb#7FXnFko4FG|m->g#{r`z_MCVrE{$HQWfO%eKq4*ZNe_7&|YNC1C69e^6$;Foh ze`BoW`zcuK2mB_D)uU;dC1Hver~WDXA-Zh%kuO1?=a--l3Ukk|cfoFp39j#EQFR|q>OVqjbng#< z%=apsyE80Rn>#u<_eXg0r#6PG({vFwD+D7*GtdTz@S$DAso4&uzAQ#=+Xxr9fferD zxwibU)V}}n;tlPKt5A=-C3aZN&joY;5Px=Ah-OX=90)&o4OOT9>wT}wb(S^oQ>)+3 z?yvh%y29w0xVF>~8XIYGJ?H}O?Z_L3^kLKCjf`Q>W_wFNzQAk%b+_5v`B~#zdmOCb1)$qhOrSbtG@kP3lE~r|MX%eMeHk-KAAk$p|YLUw|P%Y`Av@QswPFshnlbW&`qY!Q6-u zv<#ADnRGg5cnlg4BeigeobC>$BcXfm;{}5br-KE}awqc{2w}*nyP35T**Q3<@AhQH z%pdTzP{|~R~@a0wGehJP0UQ`$+K0{UStcUTUBt);3n>JF+bf&8;A>vp`0Igy- zT*iLgu~z2<(N>3=eKxQqYGeO7BeV`bvdTwAk*5QToSt%mDRHfD+O$kAi7%ANc2th| zMzcbpP_AdBr(To~c(zepw|Ls3an@{PT;yG_0}98oSW{9RedVMB()kXDaJ~t%4pK-y zk+HO3sY3`^LC8{8fQ4L>^o(Fuef@Gy?7-E#u(&dq{bQ_oo&KIpD2PZ=4@Seht7?(QdDDf%ok-6()Z#i>gW@u9Z#QV#&4fg>&-#*pb6$*JcxKu zoF-;6=09XIGJrLnEEalvJ+E#`#Cf)fir3=LGoJW2f@ZVf*X2s&D||-4sSoIB0)a@S zz4j0vXMw!2JkE={Gtm+e$Y3=ZWz>>6+5msm?|#p2)os-*8g*gnkH5QHm~v~Vq-4G{ zb2&)H4#dc%io3j6{lFbDb%OfV$F>}HjnK2>mTL1`$#o%qAwz~h{axy`CYxjn)H$43 zeB<1=l!>IJ1PH_qnBZUR^L}miwypQl!Bv1j{ypUXUHJTC)hB#8K>qSQ!)K^o*LgOr z5_`KdcGwDy2M^TXQx^nn-xB9=XGO9gOLa~US0^#1GBUol{Z%@g72{_WvUSn3Mxl0o zuc*^L%w#Av$WkI?La?O{QR=(_=Q#9qy=XY}uU+v!_0ozlg#NKv%5A4Maoa6NXCpUgniESc= z0iUfw?Iq{|34!0L?_QA2pwQ;P*7#56 zXXNi#SftN!6G}Vu@^GivBsp{*4uL+9$6E{@pw@bw)h)do?6?5{Tez9NM%dc4rwq5R03~9ld1+{UIcLBPsv+R| z5Nq}q*vLpY^(tMQw@gdd`x^J=*q)WmjK6 z&~?e}{dAYp+@nUFjLN-8y_}I11HvGc3xyhsYNYG1JFN55nh}yER8W(Fm=aEo<(*3YYo%An|RVW2ujN(lN+5Wvj>Y0|?A&Vzo!B@D!A(BzUzf z|JdBH(9YIWS}fVnY!ugk;UmAt6{Doq==3*MP2$CVdLVKURrgJku1=6u50|r;F($Q4 zs+2LWZdA{q#^8ZUUq#uuyb*AkVubJFG4ZI1S-167?Zqg^c|ZlFpnfrVhtmPADDrsj znu3Bta;0w`1n8m845i2~A@*)f-RvaUSLHU@b?n%9G`i4baz*}?|E7|Ss>DWg)y5(u z>hYbIJ8ND`o5j|cC~8HKC43$-)FXg`oH5j}vP8^0`&JYtk>6+44Llj{(r4HY`N)h; zZGYqA;h=xuT=+R^@Bu2$UZz`?v6&mhy0Q_79Y^cvDa}*i4*^vdb*kiHV)g{?*B7*S z^EJ`Gm=7WLqAVDG>5LIuE+7d=L$>;94D)>> z=%nmS+0LW5O&VtTZM0VtMLkNUB}WHF(|zZom&XE0{GH=FFM@@z9?Itnx2|XWNvF$R z&7zrk@7nZ?`I1gUM6Xb`F4jmO(|9t8LqAhJuRk{_rxCPnK%QlD)U1-4X5caCoYGRc ze4e4xupiBU8vEKb%jRygJX5pIR6G?u>*IErUR`z#$(yGB)bW_R?p2!CG`T7-m)S4& zS6Ak2j!}B)X706;z#Zl=@XnG>=kA2%6B1CT0c#$|r4gAa_IWvIqIjVCHgci_JO1wN zi7wr0+%~503O6I?W{w4AjIoouyL+-4-a~0o#qzYs8Y8)Qo~||8NxW1O-1|pmiS{$p zn;;Rz1t@oN=Wcy<=WO>82BUPL&9}1Sby;c+47yfvn))>j=NIsr<%+(qy;*vy8mYz` z<@#5qo5;_oSl6bCIMvosn`1GD%PUuBAMRXbW^H1gS4-PZj<}lU{%+iCK6E}^_3j2| zf4ON?n|G5sDP<3Z@Vn4>u-mIoxmwa}8h76&U|pLp1!(a=p_f~csF3TjftrVUgApeO zQd>+^*pzhJZ#B0LF)K8`)MN&U%hgc8u43bHc9mrYuju=dLU${!$`5;dP+!Okxn^mk zsdCrqz6KjzjXBO-@6eJ8UWIIq8-CmUXnD3$ET2$eD1To2g%$IegF^FtHN9e?3UqcF zi{j1#8ZNa}{OCfv7ek--LIgV1B&B7g^)tQO0YZxZ@lemKKc50B^CFmW_=af72g66A z@s+^K?{nh!H`nGFFwgmJK*n!In6DeTnaD1x#A$@|bf^kMU{9KLgtz+yI_`eruO5$8 z7hd>uRY5_C53hHTJ!-0DFe}bnFS=z^9vW_z9jHY<%scC~qN9-(Qy@8=|0-Pu+2qgf zwlb2m{-D&GxeL*~(V>K$Aej3cv_2hMtIJ4m?|s{1!5Jvg5h@3rJ7pg{sj%0;*6e5Z zpzWaimf7~^8_d8=)!mwFIuzW5){ls-l@?yT@kWQjKn8);q*qST_&=M4lHRFj#a`5z ztWL&f5%RsynAGjs)gimI`v|J}MQ#9mqex(iIndkfQh~3VL(J+l9bSBMt5S3#z)z=p zvy82~zU?3nw>l1=Ds7=o=t(VK-k-tR##mzR;N<7qyt45bcmZ&CdOm(q%pnz^2L@kp zbC}uEi}oliC>N~Rx60UEF%2rNNvq%!%Pw3`)Lp6j^NeL?LKaMU(as>8ew?zIOA`w%*sWh$57&|)Q3}7s zh3(b^0nxz>AAa&GYXq7&Pity&yvb&uNOhdtHj*gDWyId?Qa;0if8f14daS$>0x2I- z2hh5%ej_#eyK458Ta%ZgbnDcu62%=1xy!N%ld@E-Ev!rs+oO8U2Umh68FS(<*kCd0 zp~w=ojFB=Nr|#8Yv9-53It<5;C-zm9pJJ3499T48>Q8)%%wu$0k|ve{@2$_$u{mq1 zVv_6*L>I#$`}Iw`9-dDPE24oO1bKIkn-O z{%+0d%I&7S)~ME|o2KK_rojjo(O%sn6~VEyy}HXf*luPev!z-e%v3aTLDcwZy!S`i zZFO(M66NioM*-`D`&~<&vgW?!TysfziQqXe0x{u%%ko_m>lqB7BOQ(c=5fI) zP$Ej((S*>U$b*$YWxj}%1O4*ufmB9aYXiNqks{Jp{MtDxi^()9ALh3vp$ym~bp?r& zNYSaP)ttR#!ZiJ=9I12H#u}As6FXP6nRqGWYTU?bdHNy&=cQ%Uv9W)L3B}#}Bx8&& z5T9Y!cI$m6g@^q-4Xj4odRB6UUxXGJv}2XW9P|7eNl9M)u!qd zTl8$YX+sM%pe46HQ`!fxyGOTng2mXAU{gI8ONy*ddrqr>lurvTc?4CfHWYX@VoGER zHUxGat$sXA3vSe9_tb5F{#0HSRqUct;ZeQ2t#+|^r?$vB?G;%TXhc1)!9FZ8&$4|q zNZ{bb_4B9I0zXz0rUzJF7$}t3vb~iyctwg69n>5mi3o%5%yyLc^=db4G(el)+OU|> z4LGTE61qk&j9XGEI+wsIHKKaBluPQ%z4COJpD|@!;kWS{iKDPdNuIsm!^45+hfFm$ zG5Q>cJ_U<;dAIayOblR^YJz2V{E)~`BxPK-7mn)^s%DvHcBJft`*K{ZJgf)KZ7ss& z8VY*P!+Kt8JmOfRdspB{y0`9?X{4R0RkL|;Lt!PW@|wTUXt>xGXDkh?{f zRb!*`^N03%9bojI@o(1hS&l>K>_8_lUA|S{d{QcM3WX@R2yqfNS6(K6Q6u{=e$o z`J4miF~uOf`Bt1-XPvQXyM-OzS-tvsfty-?KD$QLW$vwB#p4hTmg94_YRVr5i2Vuy zVLSdO!a#}1o|7%wpb;Tac>fA5$oJDg6@`SQxp}i4%Pw5w1%jFe5ZkFe#IxSa3OD^U z5t(aXzMd_9NOb5CuCJhbd^!bg%GXpqB6(1dh!56o1BSL3Zcv_{1377BAiyQWcV5!I zueTW7(W_52XWAep3PGy#M>Hawt*`>;7tp<`%1};c7f6?ELx*cjZQkBi<_@AAvW_kz zg(6O@1I326DLz;_oyToqSJK>rU8R&vbAQ@fuK`?2MagUn=2}^*2FeP zMKHT3lxug7sL{GakcBbgE?+;tw7(E|!);~%4MvPLn!6}y&q0lOb114*FTyS0?$D}V z{>|#%m?h`jzDqNm+{OHN!X@816X7N6l~c(gn&zen?+i=*EfDqgt${Ed`~^@b6L{C@orSX;y_(HPSsg$a6-f| zWP2+W%Xd-SOD2hJZ-^2@=AXFUN=f%ZlM0>)_a9i}wcjp*#^YmN8`Dpuf)>Z#mHvt#@<+VyB>7pwn6E$sGsT>(H8Q+wxzk>x?w3 zPrQ=Y7JilL#V76zQg?`UG_}}Vte%RW9@V)|H{$_167;@?$ncrW+Ddz8ZTukoR8`YSRr|=`eX7jP?7jH92cmz1f@$Vc43_&B2&nlxqg8bcajh7?5Z$*O+^@kz@~WJL>5g z&MsZ7Iy_#xx3{zc)XS4plHTQS?2Ox~MYzsd5OeC8ff|bt;~a69)L{F;AyVl4j{MX) zx{}g{_6moAvNtR386FF?QxZdYqo2B}4{H<#OI%m5M^>iWQ}|;+v47z6<#}_KY^L3( zAI(f6OB===#A{RxAucd`7j%4`C4@XV* zbXt*#S9;*WHfc-Cd%ex>U>sJZVa0h?<3=H}OLzK7hp5j%7PNs#IU~OFT^5d9x7pR@`!EwQYV!Xn+vO z;ECbITeiJe0OfF5(L3I`Se66zleO39a1F}h(DicfcbycMu&s_?f+#r2;%dc9)*$^W z`B{n!wq6UA^m*7dUg?^6Y1!{nMPta9A*kZnUwn{k8g~dU3C#UUc-oGAJ#ihcC)EsE z_W7`s|#WyL~;XN)|&Eno%#uJ~kg)tkz2189pIVW1CH`3YpE4nd+5t zeG|*ClJ@bi4k#Nv(C#KqXJt^DYlW^KA2-X2NVIp}!U^J zGd~fsyfwvB5zSWTeNqDFkS!`GGJ6d)JenPY4+`}~aaHeEko7TZLl5X;F&-R5=aX2h zKbZy;^nMl7=b_#!FWcgHGwwvJ$Q--9{4OF!&r)rsx;PyYG!u0}!2USNXuL+P5+3Gb zO@4PV5VP{x%-TcE8UHD4QGoi9O4>kotiN1<*xBP@`vmm-N_TO+#?t(LhjBXZwDlqv z=cd?pjB-|FsFh#ZBGSY;xwB}4oW@gPuimU{)mm?J^sbvlcOoIfewA%AhuS`1xa#8x zalBjc`UNe$Sti}Ad+U1e+hU{TQkiRiRqXXX3YHh0XJ3#Pj$>`h$B>U&FF_}1JTddt%9+~i-k=_~D&04|_6FPN@G(`TLQLKwKkRC+ zI+qJJ9+X)#ERj>EMfIvQZnff^x7kA1OCOI<6xH}ggqYK%4B;g^a%Vk*-pj~-plTP7 zwp#dbLv3a=7od=x6_4GIt=^jQew0Tsw>PHW8CoZZIM3r&d{d`101!0{cC z%WjWXC0tgA3vo&!83D?hJCItDG~P3FM;F;J^CIlGSi zc7L%~Ya*A~w06hEJyalgJh^U^eH35t4O~GSl*bag-I}j|>2_-XC8v>=zuPz1)P3UM z=jN!Qo_q(~ zh{P0?<-I;@(Z#f=RK*B3Ofp@p3#s}1z6YQOsv_5KIoT0WTr8E@oM@>K49V5MF0QPh zZ&whz#k?6G7sqaQC36MV8*impg)9?&pObgRI&W_Ww=%xKhB9Tn`)4&}vEMMXqX0-Z z6Yc9i@Y|4NuZXZ~i%#k5FO}a-5LtQunilvMHSKW~h$6iuHw6GkI(=C!X50UQ!zm=n zm!X}inoFLXiIeVDaM}#bm9>*+LrTO}g@G|$l8x0>uM)c;3(ec;KXUywT_FKkO}n=x60TeGpJm2Azkz6RvH}b!SFh&X zpt$g9_r2}01RhhF(LaiU&Twwp2E1|RbkC)ydgUy@<%`;Kt2DDJGWaL zP3bX7$Pm@7l8nLp`{u9}N2@2FXg$3FPoaK~sNWn_ISiTP+!tYgVkB~;(dC6cDg|BM z*bZZ*RyqBoS+JjHeYj}>;t`Y_+%d8W0++P-(!9$3@Qe#nRIz7+H0Eyh?2^Jgqio9p z8Q>k1z~-N8-x=|}x(Xz49~3xXiW$Q`sJYLRTKY>m?4kr-X=`$S>?KRDJaWE82>j#l z=Up@F-s+0?be=AMS^qN>=%6{w!{Maoaw~G^A+Nri3}An-!DBA4LHwcd#hRTC64woN z<4=LsKv7T!>P{L5CI=?f3)b0^Q1Jd1X4Mtvt;fi72<_Oe)8B86&#}U`knlAih_e`= z#jIO-?ytEGnQJu6?#k`pNYh}^j@b(;OKt;m{SSK7eK8yDy+*CEXg}B^T~T^TZ=hxE zw)#dehoc}Ch#-u--c|JIvI1vhoQNx@I(*ML0q1-<`LH3I%$xe= z6OL1l{aIxjB9j|c-6ifBxS*kKPDUuHvQ9T;pgU9a8DEtZHxX7Es96tGRwWJoE_eFe z7`+zcmilSmIVUb|oSAWY-xcx9$9%}r2)0`GIT5Sz_^%}Je#42SAbic;ZSi}0#WE|7 zR3(`F9078L(yG+hR7>@IUp$?$YtLXsC7A@1wXOU4lsf~lHLGT3!QzAUXmyAoggND>?(cRbFJA5unXNed!uX|@?x6{M-l+3c`fRMI8ByBt6{2v zxojMHP|gk4ATn*wrEQm(1NE-i89;053c|_*>}>6JztDOA*f#C63ArOvKuRu{ds1DX zx5EYaq#Hm14T@~4#O~o1Y}hqQu#tnm(#4odlG1S)QLzh=oK~PqYgW~ z^FaM+ZVP>P$$8!^FUo&=r-We0Z9@b-LH}*Xin^PZ+y*l-1 zo7VCU?$8lmY(k04Jw(|>Yv1c0ThGZ9F#>1@gfNPMp4*=lIFiE{y|wB1Y^@Tu+|>5m zHh59D!~(V36Fs)fv-Hag=KwssNc2UJmFV13gLynzZFYh{4dC>(B^?SdBM@Tr4mK=( zrNE2wHNAlHIqxm=kledkz%M`rHDK%-CrHRdi|yD&OB}d(7MCd{u2onJ!~=So8S$41 zrx>}XaB#1O` zujA(%-g(ChnZM!_TkAZ#vsYysvsO74G;-6uYJ(jvZZqsa7decbe=5t?{PZOP-eVJC z3_qu@+cPp59pen2eGl1ynQf3PEZUyO`@hwt5_{ zkvUXi5^R14E<$}Zc4XDhPVIee_bz4?Yla|uM$;R)_ts7$BsauR`nFhz3B$&`r4A7# zu+mUZ%#p|+qNE#OG3kmW?rS$MDZ!iY>E+I)&rls~LHkpBD#+ z)0J_;op!q}$VrOM-Q3rSVTJ4=S(ESroB9Wxito_R5!TZ>4JaGfN4(}#E0FBZsa1I2 z;MmNm1CbkpiW=rK;te!%tHtm6m6s(Qc=hlyGjrU3NbZ9Z%TK2t5^ENLI24`R@(T_YuK%eRD2{kBQEcQBZKHpQ_^A zx!DD#D~Wsr4fu&k)O(-|DH!Jp65d&+|5^zEc)=RLmNzW?fCJ)3>^#%awZ$7`{{muo zj?W8lB)wf!Sg72`0c-MPk6lXF+(M@}77LTeNuad-978MFqjso zA+E)`0SwBWfF{+kDeZx<=yqlE;nZvm_-vi7Twi}^JDs>V*rHRN03=QHO4_>s$t$(% zzPwJ`Ld@#%4_|%yZzG(}QIHpR!@?M8lOy77X zUC%y52}t1j@UCjhTR}tA(4L;2iL9aC7*n={wy0JBdcM7Gm-jyq5_JLP7a~iFC zI@g{}R_mY#l?D={MKlZ{>l*m-IHqLKY5Uid`#VN4R@twyDw1jz(d#*7qGfgtt`au2 zHA;;^p$KpWc-7EN!pz41Q%BCl4@Wf{RNgh%p(j3p!{A9PZ{vEKCf6@DH^03LG-(>( zRL9OyC4m3Wl$|B=@7N$B;}j+phjuC7;I#KfqMjx&Nz+V^n>Z)s}R=aQ`k0dXIE0FGnC;X zLbj=bBXm+=Gsl#Yn^1e`*fHdto8WF1SZs+s6vP$?p-V3;TL?^#0F*|$OVjBOAU-&< zVqd~5Bd3WnwXjssLG6Kw^deccV0^owNx^^`OJZBd%qg!z=gUQ z*IF7Kg#s<;p{mkBQfNRdmw0L%m$=#dAoE@x`GI#)1D0-pX$UqQ#T}1t1%Hj!#7sZ* zOQs+AKWF-Vbs}Cju3h6sPptqrZWPHAC%FbkT3VmC_re>To?JiN;b?z7AaZMnSLMU! zPsOzdE&CQ~WG?Nq@6d$ecXKDx-Qfmu+Ru=5o8rK=(dM1`CXqhFDIM7bboE|yp6;F&2P%-q} z-;x0%jI1_Ag@pmxi7iWzT8;(pWLwBA|6w#&rA@@Jvrm3%ZkH@eRrVB^cqpL6rs!ve zO7r;ntF^XqZW+5(kF&tpbyJOkAeNfgi+hnAdO3hzySHV`{(y{ZyKdc+9J1S3f<>8W zPSB&xn=YSkb)A?QV0q2JPDB{3SCuh!*mQKLPDjnMWm}N=Rp5 zSba!jKKv5iyr0q!#ugA4?0+&qEY!qLThbjIAa<}h=@>vgU=eV>sK`~J2DI^<1ktk4 zndO{Qo9p683jM29Bzl^}U=7F>m}+hcO>SwWHJSI$Z?!oC1|^wzi65ku|FHr##@0M= zCeu^D|9LXvFvy8tjm_N^O9K>8>zi)Q@THYVm7_ds>1a8&g@IMD!~oiR-p)nCk{pZ^ z7hakQT+pi#F!FZ=8zK7OJMHN; zTlZP$D>fECvm0YgXHAPuj4W})UX)=8mfBr^TbW0>%lSEH zc5kOpKZe^FMMDyc$Ykp!knOrBS_!GwgK16Pc{4BAhIDu}Z>j@b=bZPrXr_qj$-`=%e-2Zv`;Gu#)_O z&!GMdpRu<1_P0|)3R2#a9t@x~{-~Jyd}^wK{+;sUp3VaQU<45amJ905?Q>Igo4YBT z>FotN@K~(6LKzFRWD~qF>KAh#FyAwHoRQTc()0W&FAwj8k(U0MobhZQdGgyf-~kuz z2>m?dBps*{nAML3PhjN&$Lp0iLmDU~2M2Pg)9150y~!l-@9oRy1-lSLui!I;cj)w2?TkYhM z&DswXH#^yM6{cw%5^g71mdAPIc`o-`VwM78|5$t;z<45Y78SuhVGhP6IDUS)0?B~d z`z1Pe=<4Vn=}aD-Hf}r*Exx%Jj2Ce&x!uK;{2D;mN*t`I;p0VU;NH1-N$Wu&d^EU2 zRMkE9Y3UFWxbk9LR&8BdcBGNsBL|^_bKQtKB4FQlQ83!MIF45c4j~nDK&Iw;x`;Frffx@D01$V-MAquqS{C6D{aQx0B6TGvv2bqa>vvZ1+m*_H0^ms|Mn^B?j12;fdT+TYK4HSE zTK;MQton__8WlPQJ-LeL^~_lM=(rA6zRkh4_iQX9hOHF~IXMPAlR=0=llH_DpDPXu z@!hxBWl-K$YD=N-$ahFZ7rc3Z!PJ8c!=&s2`@$p0Kh`BM@&>uiUq7`DO5-s8gU z>I%Ir?Jp-G6v@(yH(hfhG}7tvhvrVyZEu%19!6(yS%qDuDOEyv&Ut7ZS$$S9r*mM% z!Dp501Gdw>?g>n0RH8*xb1SR;={*!5Bomrkjm(({-$I+}~$AQ^#E+~0NFkqFwTHirms>^f$MnPx*km3CGf){ph6xBlwj z80!%(rPuE+YrNmj=^`|H_3*$EpYji$J;UflR3d6Lg?h*4Ps|+=$&J(ppZamcyC+9P za;oVJ{RPgq@06j!L?%&r>v2~*ZHIGLb=%|eI`yb(prFp#5@{$O zxno$1Y!TBCgur4ru2Sm{Bl`j8z!O+*NH zj}(DVc`;Vp-SGCP9wdSGfO^l7cWS|P(TpALrz|H=N#Lakv2P0)&T)R~uj)L#a!t)` zmvJ@NA6k7v0#plW84ErJ3&)GeOy$I`!Tq zm$+6g$_=zsrT>Qnbj~x;_AvAGhb5YZHBFxPrQ(WCAQ(JLT!g$cBj|*&s!EaChfJpg zN>PEL4#i_L9yO|{#6tIoou|gdQ^moH-FfNs5ZzGf+N#FqR;6*XMQI2CNfB|k`iRL| zMx%YWgKL1365bUMcX8@{F4f7i)`x~nG>dZChk;b4A7HD+H`Mp$ZGF2~6Ad@L%m@br z0Bi)*SDHsHMPFy`WT(YSeO{9?9+1Fze>zdQSW{uJ?`)T(yM_c-*0X|A0BXcaf>ch) zD|I-z36itfNzr(QG}?8|Scs%3)reA4an#haIy$FEiVkIIKWtjPv*L0;PCNDjLFAZv zxHKi2tiu2TFUm80mL*fY+emM1&qfviap!y zEFOSo4>xvwNkGxFgY2+o>V%EKQf$dNZIFKZ^LL1>32?A|r0VdnN%iE9@b?`6*$p(C zf9Dhb)@-)Qyc;J0w9LbQ;SaI{HdLlBmP- z-nNt?{oPy;gHNZTD3z~0WFEoNSD4|Rw%{>dcWeS^e-+o~x*(?S>5(@CB?#RIDE0Hn z@SQymH3rvIvO-4Brb|5bD`p@Z4HUweBLDh0BMm<6OuEQ?K_i1Dc~84~qcc_EoQW^Y zGOh)gZ1R9Xo!uB>k*zH$3X04zs4VGxYoWA&>t6SGG2`l4WU;}5 zzu#-SIZ~&I_)J76uqkL3v)wVO5sfyQ9ucbQTyBizMjC_+3a54uXw{Z?5s(AXGMzkv zw^~{yz=b$gIuizs;s`s#(wTQ@o>c|CK9Ig$QF3?kB&nW@)-a~my_(qW$fi8THZWV_ zS%k@p0ioH3w$q-f-4)jmYy}|p6EH4hej~Kgx~~{$FwZ%IGnZN+M*Vb zSvPFi_&^b#3%oE91C4G{fvYOaeR|n?by7pQkhz0xwIF5MLPHyK&G0(4!-S84stzFp zBdonMdTyJ8sQIE?~bS^tiaBF5hLB(EwdGOaa=fbYtMD4QF= zZHDI7t`dh*LpKV5d{(*r?LZ9m_@d;Bnet*i)JtUFuHi7aS89H!cQLCIwo^Q0lU;9P zuG7eL9kXGpwzt*j*S}J)7H1C2T7beYla(HCp3MdV zn}(QWI}#WT^k&g{R&SCe`8*Hm$MkX(8+Fg z>s7G^$O}E26D4s8 zJ8nVW5X?`dbj}e`5VL1~pTKI&;*J6A)Sr(yARI(F-gG%hKJ`&ZzkE6|Hj;(ouA~7E zkn;vCBE#g2APi_{kko4GWV?CK$2ThlS-Mq^8vHZ27h9k4hXW0e3ykiW>s)3v2m6(~ zw%N=3K+48^7j~5w{#svOdysMM)>xXxTgcA5AbV}LPhVlTO>Fk3c2xONdkEUk;7cl(q+wR_g8jZjw<14rvv^t`$k8e61XyOXx*3L-eb(&p{5n3e>ui ziifXk`#nWTY+cQMTRm*VN(;Nqas&A}Sn?M83(yBUyRr7DlXItCg6)UKflN2BO}d>z ze2!aqr1l6iKflHr*K5sO?Msq-n<-XLhXOHksbS+C-g{R+#B;@N%mZ`a=R1pBf9?mR zLvk7uEe>b07T*aaB8rQbJ+h}G`4)(?Jq@Gv=<@VeGo(~cUzb?-t(2*S=GYr~<@bMC0-(p^3)GBN}y_U@i`a32{2xtHl4mn zb_+OHW=I}yAfzJLG;(Iqq9w=JcKzdW{2QT(!aXExRLX!9V)tyfQUgN?ssx>RUzT&f zw`LsBPKb=B&zLtbBuvdpfqL7@Hu`Mz+vMG<@pAQfCyq#pygo|atBdy*SutcllTZ@

tqqpg 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 From 18a9efef9b4bf10bfa63bdc711e6580755158877 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Mon, 29 Jun 2020 16:47:14 +0200 Subject: [PATCH 09/18] Update readme with backward compatibility --- .../swagger/diff/output/MarkdownRender.java | 484 ++++++++++-------- 1 file changed, 267 insertions(+), 217 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index 31017b3a..0cbad1c1 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -14,224 +14,274 @@ import io.swagger.models.HttpMethod; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; +import j2html.tags.ContainerTag; +import sun.jvm.hotspot.oops.Mark; + +import static j2html.TagCreator.*; public class MarkdownRender implements Render { - final String H3 = "### "; - final String H2 = "## "; - final String BLOCKQUOTE = "> "; - final String CODE = "`"; - final String PRE_CODE = " "; - final String PRE_LI = " "; - final String LI = "* "; - final String HR = "---\n"; - - public MarkdownRender() {} - - public String render(SwaggerDiff diff) { - List newEndpoints = diff.getNewEndpoints(); - String ol_newEndpoint = ol_newEndpoint(newEndpoints); - - List missingEndpoints = diff.getMissingEndpoints(); - String ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); - - List changedEndpoints = diff.getChangedEndpoints(); - String ol_changed = ol_changed(changedEndpoints); - - return renderHtml(diff.getOldVersion(), diff.getNewVersion(), ol_newEndpoint, ol_missingEndpoint, ol_changed); - } - - public String renderHtml(String oldVersion, String newVersion, String ol_new, String ol_miss, - String ol_changed) { - StringBuffer sb = new StringBuffer(); - sb.append(H2).append("Version " + oldVersion + " to " + newVersion).append("\n").append(HR); - sb.append(H3).append("What's New").append("\n").append(HR) - .append(ol_new).append("\n").append(H3) - .append("What's Deprecated").append("\n").append(HR) - .append(ol_miss).append("\n").append(H3) - .append("What's Changed").append("\n").append(HR) - .append(ol_changed); - return sb.toString(); - } - - private String ol_newEndpoint(List endpoints) { - if (null == endpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (Endpoint endpoint : endpoints) { - sb.append(li_newEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return sb.toString(); - } - - private String li_newEndpoint(String method, String path, String desc) { - StringBuffer sb = new StringBuffer(); - sb.append(LI).append(CODE).append(method).append(CODE) - .append(" " + path).append(" " + desc + "\n"); - return sb.toString(); - } - - private String ol_missingEndpoint(List endpoints) { - if (null == endpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (Endpoint endpoint : endpoints) { - sb.append(li_newEndpoint(endpoint.getMethod().toString(), - endpoint.getPathUrl(), endpoint.getSummary())); - } - return sb.toString(); - } - - private String ol_changed(List changedEndpoints) { - if (null == changedEndpoints) return ""; - StringBuffer sb = new StringBuffer(); - for (ChangedEndpoint changedEndpoint : changedEndpoints) { - String pathUrl = changedEndpoint.getPathUrl(); - Map changedOperations = changedEndpoint - .getChangedOperations(); - for (Entry entry : changedOperations - .entrySet()) { - String method = entry.getKey().toString(); - ChangedOperation changedOperation = entry.getValue(); - String desc = changedOperation.getSummary(); - - StringBuffer ul_detail = new StringBuffer(); - if (changedOperation.isDiffParam()) { - ul_detail.append(PRE_LI).append("Parameters") - .append(ul_param(changedOperation)); - } - if (changedOperation.isDiffProp()) { - ul_detail.append(PRE_LI).append("Return Type") - .append(ul_response(changedOperation)); - } - sb.append(CODE).append(method).append(CODE) - .append(" " + pathUrl).append(" " + desc + " \n") - .append(ul_detail); - } - } - return sb.toString(); - } - - private String ul_response(ChangedOperation changedOperation) { - List addProps = changedOperation.getAddProps(); - List delProps = changedOperation.getMissingProps(); - List changedProps = changedOperation.getChangedProps(); - StringBuffer sb = new StringBuffer("\n\n"); - - String prefix = PRE_LI + PRE_CODE; - for (ElProperty prop : addProps) { - sb.append(PRE_LI).append(PRE_CODE).append(li_addProp(prop) + "\n"); - } - for (ElProperty prop : delProps) { - sb.append(prefix).append(li_missingProp(prop) + "\n"); - } - for (ElProperty prop : changedProps) { - sb.append(prefix).append(li_changedProp(prop) + "\n"); - } - return sb.toString(); - } - - private String li_missingProp(ElProperty prop) { - Property property = prop.getProperty(); - StringBuffer sb = new StringBuffer(""); - sb.append("Delete ").append(prop.getEl()) - .append(null == property.getDescription() ? "" - : (" //" + property.getDescription())); - return sb.toString(); - } - - private String li_addProp(ElProperty prop) { - Property property = prop.getProperty(); - StringBuffer sb = new StringBuffer(""); - sb.append("Insert ").append(prop.getEl()) - .append(null == property.getDescription() ? "" - : (" //" + property.getDescription())); - return sb.toString(); - } - - private String li_changedProp(ElProperty prop) { - Property property = prop.getProperty(); - String prefix = "Modify "; - String desc = " //" + property.getDescription(); - String postfix = (null == property.getDescription() ? "" : desc); - - StringBuffer sb = new StringBuffer(""); - sb.append(prefix).append(prop.getEl()) - .append(postfix); - return sb.toString(); - } - - private String ul_param(ChangedOperation changedOperation) { - List addParameters = changedOperation.getAddParameters(); - List delParameters = changedOperation.getMissingParameters(); - List changedParameters = changedOperation - .getChangedParameters(); - StringBuffer sb = new StringBuffer("\n\n"); - for (Parameter param : addParameters) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_addParam(param) + "\n"); - } - for (ChangedParameter param : changedParameters) { - List increased = param.getIncreased(); - for (ElProperty prop : increased) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_addProp(prop) + "\n"); - } - } - for (ChangedParameter param : changedParameters) { - boolean changeRequired = param.isChangeRequired(); - boolean changeDescription = param.isChangeDescription(); - if (changeRequired || changeDescription) sb.append(PRE_LI) - .append(PRE_CODE).append(li_changedParam(param) + "\n"); - } - for (ChangedParameter param : changedParameters) { - List missing = param.getMissing(); - List changed = param.getChanged(); - for (ElProperty prop : missing) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_missingProp(prop) + "\n"); - } - for (ElProperty prop : changed) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_changedProp(prop) + "\n"); - } - } - for (Parameter param : delParameters) { - sb.append(PRE_LI).append(PRE_CODE) - .append(li_missingParam(param) + "\n"); - } - return sb.toString(); - } - - private String li_addParam(Parameter param) { - StringBuffer sb = new StringBuffer(""); - sb.append("Add ").append(param.getName()) - .append(null == param.getDescription() ? "" - : (" //" + param.getDescription())); - return sb.toString(); - } - - private String li_missingParam(Parameter param) { - StringBuffer sb = new StringBuffer(""); - sb.append("Delete ").append(param.getName()) - .append(null == param.getDescription() ? "" - : (" //" + param.getDescription())); - return sb.toString(); - } - - private String li_changedParam(ChangedParameter changeParam) { - boolean changeRequired = changeParam.isChangeRequired(); - boolean changeDescription = changeParam.isChangeDescription(); - Parameter rightParam = changeParam.getRightParameter(); - Parameter leftParam = changeParam.getLeftParameter(); - StringBuffer sb = new StringBuffer(""); - sb.append(rightParam.getName()); - if (changeRequired) { - sb.append(" change into " + (rightParam.getRequired() ? "required" : "not required")); - } - if (changeDescription) { - sb.append(" Notes ").append(leftParam.getDescription()).append(" change into ") - .append(rightParam.getDescription()); - } - return sb.toString(); - } + private static final String NON_BACKWARDS_CHANGES = "Non-backwards changes"; + + private boolean showBackwardsIncompatibilities; + + final String H3 = "### "; + + final String H2 = "## "; + + final String BLOCKQUOTE = "> "; + + final String CODE = "`"; + + final String PRE_CODE = " "; + + final String PRE_LI = " "; + + final String LI = "* "; + + final String HR = "---\n"; + + public MarkdownRender() { + } + + public String render(SwaggerDiff diff) { + List newEndpoints = diff.getNewEndpoints(); + String ol_newEndpoint = ol_newEndpoint(newEndpoints); + + List missingEndpoints = diff.getMissingEndpoints(); + String ol_missingEndpoint = ol_missingEndpoint(missingEndpoints); + + List changedEndpoints = diff.getChangedEndpoints(); + String ol_changed = ol_changed(changedEndpoints); + + return renderHtml(diff.getOldVersion(), diff.getNewVersion(), ol_newEndpoint, ol_missingEndpoint, ol_changed); + } + + public String renderHtml(String oldVersion, String newVersion, String ol_new, String ol_miss, + String ol_changed) { + StringBuffer sb = new StringBuffer(); + sb.append(H2).append("Version " + oldVersion + " to " + newVersion).append("\n").append(HR); + sb.append(H3).append("What's New").append("\n").append(HR) + .append(ol_new).append("\n").append(H3) + .append("What's Missing").append("\n").append(HR) + .append(ol_miss).append("\n").append(H3) + .append("What's Changed").append("\n").append(HR) + .append(ol_changed); + return sb.toString(); + } + + public MarkdownRender withBackwardsIncompatibilities() { + showBackwardsIncompatibilities = true; + return this; + } + + private String ol_newEndpoint(List endpoints) { + if (null == endpoints) + return ""; + StringBuffer sb = new StringBuffer(); + for (Endpoint endpoint : endpoints) { + sb.append(li_newEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return sb.toString(); + } + + private String li_newEndpoint(String method, String path, String desc) { + StringBuffer sb = new StringBuffer(); + sb.append(LI).append(CODE).append(method).append(CODE) + .append(" " + path).append(" " + desc + "\n"); + return sb.toString(); + } + + private String li_missingEndpoint(String method, String path, String desc) { + StringBuffer sb = new StringBuffer(); + sb.append(LI).append(CODE).append(method).append(CODE) + .append(" " + path).append(i_backwardsIncompatibilitiesWarning()).append(" " + desc + "\n"); + return sb.toString(); + } + + private String ol_missingEndpoint(List endpoints) { + if (null == endpoints) + return ""; + StringBuffer sb = new StringBuffer(); + for (Endpoint endpoint : endpoints) { + sb.append(li_missingEndpoint(endpoint.getMethod().toString(), + endpoint.getPathUrl(), endpoint.getSummary())); + } + return sb.toString(); + } + + private String ol_changed(List changedEndpoints) { + if (null == changedEndpoints) + return ""; + StringBuffer sb = new StringBuffer(); + for (ChangedEndpoint changedEndpoint : changedEndpoints) { + String pathUrl = changedEndpoint.getPathUrl(); + Map changedOperations = changedEndpoint + .getChangedOperations(); + for (Entry entry : changedOperations + .entrySet()) { + String method = entry.getKey().toString(); + ChangedOperation changedOperation = entry.getValue(); + String desc = changedOperation.getSummary(); + + StringBuffer ul_detail = new StringBuffer(); + if (changedOperation.isDiffParam()) { + ul_detail.append(PRE_LI).append("Parameters") + .append(ul_param(changedOperation)); + } + if (changedOperation.isDiffProp()) { + ul_detail.append(PRE_LI).append("Return Type") + .append(ul_response(changedOperation)); + } + sb.append(CODE).append(method).append(CODE) + .append(" " + pathUrl); + if (!changedEndpoint.isBackwardsCompatible()) { + sb.append(i_backwardsIncompatibilitiesWarning()); + } + sb.append(" " + desc + " \n") + .append(ul_detail); + } + } + return sb.toString(); + } + + private String ul_response(ChangedOperation changedOperation) { + List addProps = changedOperation.getAddProps(); + List delProps = changedOperation.getMissingProps(); + List changedProps = changedOperation.getChangedProps(); + StringBuffer sb = new StringBuffer("\n\n"); + + String prefix = PRE_LI + PRE_CODE; + for (ElProperty prop : addProps) { + sb.append(PRE_LI).append(PRE_CODE).append(li_addProp(prop) + "\n"); + } + for (ElProperty prop : delProps) { + sb.append(prefix).append(li_missingProp(prop) + "\n"); + } + for (ElProperty prop : changedProps) { + sb.append(prefix).append(li_changedProp(prop) + "\n"); + } + return sb.toString(); + } + + private String li_missingProp(ElProperty prop) { + Property property = prop.getProperty(); + StringBuffer sb = new StringBuffer(""); + sb.append("Delete ").append(prop.getEl()) + .append(null == property.getDescription() ? "" + : (" //" + property.getDescription())) + .append(i_backwardsIncompatibilitiesWarning()); + return sb.toString(); + } + + private String li_addProp(ElProperty prop) { + Property property = prop.getProperty(); + StringBuffer sb = new StringBuffer(""); + sb.append("Insert ").append(prop.getEl()) + .append(null == property.getDescription() ? "" + : (" //" + property.getDescription())); + if (prop.getProperty() != null && prop.getProperty().getRequired()) { + sb.append(" required").append(i_backwardsIncompatibilitiesWarning()); + } + return sb.toString(); + } + + private String li_changedProp(ElProperty prop) { + Property property = prop.getProperty(); + String prefix = "Modify "; + String desc = " //" + property.getDescription(); + String postfix = (null == property.getDescription() ? "" : desc); + + StringBuffer sb = new StringBuffer(""); + sb.append(prefix).append(prop.getEl()) + .append(postfix) + .append(i_backwardsIncompatibilitiesWarning()); + return sb.toString(); + } + + private String ul_param(ChangedOperation changedOperation) { + List addParameters = changedOperation.getAddParameters(); + List delParameters = changedOperation.getMissingParameters(); + List changedParameters = changedOperation + .getChangedParameters(); + StringBuffer sb = new StringBuffer("\n\n"); + for (Parameter param : addParameters) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_addParam(param) + "\n"); + } + for (ChangedParameter param : changedParameters) { + List increased = param.getIncreased(); + for (ElProperty prop : increased) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_addProp(prop) + "\n"); + } + } + for (ChangedParameter param : changedParameters) { + boolean changeRequired = param.isChangeRequired(); + boolean changeDescription = param.isChangeDescription(); + if (changeRequired || changeDescription) + sb.append(PRE_LI) + .append(PRE_CODE).append(li_changedParam(param) + "\n"); + } + for (ChangedParameter param : changedParameters) { + List missing = param.getMissing(); + List changed = param.getChanged(); + for (ElProperty prop : missing) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_missingProp(prop) + "\n"); + } + for (ElProperty prop : changed) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_changedProp(prop) + "\n"); + } + } + for (Parameter param : delParameters) { + sb.append(PRE_LI).append(PRE_CODE) + .append(li_missingParam(param) + "\n"); + } + return sb.toString(); + } + + private String li_addParam(Parameter param) { + StringBuffer sb = new StringBuffer(""); + sb.append("Add ").append(param.getName()) + .append(null == param.getDescription() ? "" + : (" //" + param.getDescription())); + return sb.toString(); + } + + private String li_missingParam(Parameter param) { + StringBuffer sb = new StringBuffer(""); + sb.append("Delete ").append(param.getName()) + .append(null == param.getDescription() ? "" + : (" //" + param.getDescription())) + .append(i_backwardsIncompatibilitiesWarning()); + return sb.toString(); + } + + private String li_changedParam(ChangedParameter changeParam) { + boolean changeRequired = changeParam.isChangeRequired(); + boolean changeDescription = changeParam.isChangeDescription(); + Parameter rightParam = changeParam.getRightParameter(); + Parameter leftParam = changeParam.getLeftParameter(); + StringBuffer sb = new StringBuffer(""); + sb.append(rightParam.getName()); + if (changeRequired) { + sb.append(" change into " + (rightParam.getRequired() ? "required" : "not required")); + } + if (changeDescription) { + sb.append(" Notes ").append(leftParam.getDescription()).append(" change into ") + .append(rightParam.getDescription()); + } + if (!changeParam.isBackwardsCompatible()) { + sb.append(i_backwardsIncompatibilitiesWarning()); + } + return sb.toString(); + } + + /** Add icon if modifications make backwards incompatibilies. */ + private String i_backwardsIncompatibilitiesWarning() { + return showBackwardsIncompatibilities ? " ❗ " : null; + } } From 1e138cbf597fda2e617ea279a71444e7b9ee8db9 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Mon, 29 Jun 2020 16:50:17 +0200 Subject: [PATCH 10/18] Update gitignore --- .gitignore | 2 +- testDiff.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 testDiff.md diff --git a/.gitignore b/.gitignore index 97d087b2..7a97aed4 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,6 @@ hs_err_pid* /target/ \.DS_Store *.html -testDiff\.md *.swp .idea +*.iml diff --git a/testDiff.md b/testDiff.md new file mode 100644 index 00000000..3764b276 --- /dev/null +++ b/testDiff.md @@ -0,0 +1,71 @@ +## Version 1.0.0 to 1.0.2 +--- +### What's New +--- +* `GET` /pet/{petId} Find pet by ID + +### What's Deprecated +--- +* `POST` /pet/{petId} Updates a pet in the store with form data + +### What's Changed +--- +`POST` /pet Add a new pet to the store + Parameters + + Add tags //add new query param demo + Insert body.newFeild //a feild demo by sayi + Insert body.category.newCatFeild + Insert body.owner.newUserFeild //a new user feild demo + Delete body.category.name + Delete body.owner.phone + Modify body.name +`PUT` /pet Update an existing pet + Parameters + + Insert body.newFeild //a feild demo by sayi + Insert body.category.newCatFeild + Insert body.owner.newUserFeild //a new user feild demo + Delete body.category.name + Delete body.owner.phone + Modify body.name +`DELETE` /pet/{petId} Deletes a pet + Parameters + + Add newHeaderParam +`POST` /pet/{petId}/uploadImage uploads an image for pet + Parameters + + Add petId //ID of pet to update, default false + petId change into not required Notes ID of pet to update change into ID of pet to update, default false +`POST` /user Create user + Parameters + + Insert body.newUserFeild //a new user feild demo + Insert body.favorite.newFeild //a feild demo by sayi + Insert body.favorite.category.newCatFeild + Delete body.phone + Delete body.favorite.category.name + Modify body.favorite.name +`GET` /user/login Logs user into the system + Parameters + + Delete password //The password for login in clear text +`PUT` /user/{username} Updated user + Parameters + + Insert body.newUserFeild //a new user feild demo + Insert body.favorite.newFeild //a feild demo by sayi + Insert body.favorite.category.newCatFeild + Delete body.phone + Delete body.favorite.category.name + Modify body.favorite.name +`GET` /user/{username} Get user by user name + Return Type + + Insert newUserFeild //a new user feild demo + Insert favorite.newFeild //a feild demo by sayi + Insert favorite.category.newCatFeild + Delete phone + Delete favorite.category.name + Modify favorite.name From 2c56d5330e2b5c4a9456bae7e90de18c900b3129 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Tue, 30 Jun 2020 11:52:30 +0200 Subject: [PATCH 11/18] Reformat with default style --- pom.xml | 367 +++++++++--------- .../deepoove/swagger/diff/SwaggerDiff.java | 40 +- .../com/deepoove/swagger/diff/cli/CLI.java | 17 +- .../com/deepoove/swagger/diff/cli/Regex.java | 6 +- .../swagger/diff/cli/RegexValidator.java | 12 +- .../swagger/diff/compare/ListDiff.java | 9 +- .../swagger/diff/compare/MapKeyDiff.java | 7 +- .../swagger/diff/compare/ModelDiff.java | 186 ++++----- .../swagger/diff/compare/ParameterDiff.java | 28 +- .../swagger/diff/compare/PropertyDiff.java | 77 ++-- .../diff/compare/SpecificationDiff.java | 29 +- .../swagger/diff/model/ChangedEndpoint.java | 10 +- .../swagger/diff/model/ChangedOperation.java | 212 +++++----- .../swagger/diff/model/ChangedParameter.java | 84 ++-- .../swagger/diff/model/ElProperty.java | 2 +- .../swagger/diff/output/HtmlRender.java | 29 +- .../swagger/diff/output/MarkdownRender.java | 23 +- .../deepoove/swagger/diff/output/Render.java | 4 +- .../com/deepoove/swagger/test/CLITest.java | 33 +- ...gerDiffBackwardsIncompatibilitiesTest.java | 13 +- .../swagger/test/SwaggerDiffTest.java | 276 ++++++------- ...tstore_v2_withAddRequiredPropertyBody.json | 4 +- ..._v2_withAllBackwardsIncompatibilities.json | 6 +- ...e_v2_withChangePropertyBodyToRequired.json | 6 +- .../petstore_v2_withChangesCompatibles.json | 1 - 25 files changed, 726 insertions(+), 755 deletions(-) diff --git a/pom.xml b/pom.xml index 780bac03..ca6c3f1f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,202 +1,201 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - - org.sonatype.oss - oss-parent - 7 - + + org.sonatype.oss + oss-parent + 7 + - com.deepoove - swagger-diff - 1.2.1 - jar + com.deepoove + swagger-diff + 1.2.1 + jar - swagger-diff - diff swagger api document - http://maven.apache.org + swagger-diff + diff swagger api document + http://maven.apache.org - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + - - - Sayi - adasai90@gmail.com - - + + + Sayi + adasai90@gmail.com + + + + scm:git:git@github.com:Sayi/swagger-diff.git + scm:git:git@github.com:Sayi/swagger-diff.git + git@github.com:Sayi/swagger-diff.git + - - scm:git:git@github.com:Sayi/swagger-diff.git - scm:git:git@github.com:Sayi/swagger-diff.git - git@github.com:Sayi/swagger-diff.git - + + UTF-8 + - - UTF-8 - + + + oss + https://oss.sonatype.org/content/repositories/snapshots/ + + + oss + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + - - - oss - https://oss.sonatype.org/content/repositories/snapshots/ - - - oss - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - io.swagger - swagger-parser - 1.0.34 - - - io.swagger - swagger-compat-spec-parser - 1.0.34 - - - com.j2html - j2html - 1.2.0 - - - com.alibaba - fastjson - 1.2.31 - - - com.beust - jcommander - 1.72 - - - junit - junit - 4.8.2 - test - + + + io.swagger + swagger-parser + 1.0.34 + + + io.swagger + swagger-compat-spec-parser + 1.0.34 + + + com.j2html + j2html + 1.2.0 + + + com.alibaba + fastjson + 1.2.31 + + + com.beust + jcommander + 1.72 + + + junit + junit + 4.8.2 + test + org.projectlombok lombok 1.16.20 - + - - - release - - - - maven-compiler-plugin - - 1.8 - 1.8 - UTF-8 - - 3.2 - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - true - true - true - true - true - - - - attach-javadocs - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - verify - - sign - - - - - - - - - oss - https://oss.sonatype.org/content/repositories/snapshots/ - - - oss - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - - - maven-compiler-plugin - - 1.8 - 1.8 - UTF-8 - - 3.2 - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - xml - 256m - - true - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - + + + release + + + + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + 3.2 + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + true + true + true + true + true + + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + verify + + sign + + + + + + + + + oss + https://oss.sonatype.org/content/repositories/snapshots/ + + + oss + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + 3.2 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + xml + 256m + + true + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + diff --git a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java index c43eda6a..9a246f28 100644 --- a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java @@ -32,10 +32,8 @@ public class SwaggerDiff { /** * compare two swagger 1.x doc * - * @param oldSpec - * old api-doc location:Json or Http - * @param newSpec - * new api-doc location:Json or Http + * @param oldSpec old api-doc location:Json or Http + * @param newSpec new api-doc location:Json or Http */ public static SwaggerDiff compareV1(final String oldSpec, final String newSpec) { return compare(oldSpec, newSpec, null, null); @@ -44,17 +42,15 @@ public static SwaggerDiff compareV1(final String oldSpec, final String newSpec) /** * compare two swagger v2.0 doc * - * @param oldSpec - * old api-doc location:Json or Http - * @param newSpec - * new api-doc location:Json or Http + * @param oldSpec old api-doc location:Json or Http + * @param newSpec new api-doc location:Json or Http */ public static SwaggerDiff compareV2(final String oldSpec, final String newSpec) { return compare(oldSpec, newSpec, null, SWAGGER_VERSION_V2); } public static SwaggerDiff compare(String oldSpec, String newSpec, - List auths, String version) { + List auths, String version) { return new SwaggerDiff(oldSpec, newSpec, auths, version).compare(); } @@ -65,7 +61,7 @@ public static SwaggerDiff compare(String oldSpec, String newSpec, * @param version */ private SwaggerDiff(final String oldSpec, final String newSpec, final List auths, - final String version) { + final String version) { if (SWAGGER_VERSION_V2.equals(version)) { SwaggerParser swaggerParser = new SwaggerParser(); oldSpecSwagger = swaggerParser.read(oldSpec, auths, true); @@ -80,17 +76,17 @@ private SwaggerDiff(final String oldSpec, final String newSpec, final List { @@ -27,15 +26,13 @@ public static ListDiff diff(List left, List right) { } /** - * * @param left * @param right - * @param biFunc - * if right List contains left element + * @param biFunc if right List contains left element * @return */ public static ListDiff diff(List left, List right, - BiFunction, K, K> biFunc) { + BiFunction, K, K> biFunc) { ListDiff instance = new ListDiff<>(); if (null == left && null == right) return instance; if (null == left) { diff --git a/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java index 915faeb7..924ca6a4 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/MapKeyDiff.java @@ -1,18 +1,17 @@ package com.deepoove.swagger.diff.compare; +import lombok.Getter; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import lombok.Getter; - /** * compare two Maps by key - * + * * @author Sayi - * @version */ @Getter public class MapKeyDiff { diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java index 6f8dcc63..2b5708b8 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java @@ -11,110 +11,110 @@ /** * compare two model + * * @author Sayi - * @version */ @Data public class ModelDiff { private List increased; private List missing; - private List changed; - private List requiredChanges = new ArrayList(); - private List typeChanges = new ArrayList(); + private List changed; + private List requiredChanges = new ArrayList(); + private List typeChanges = new ArrayList(); Map oldDedinitions; Map newDedinitions; - private ModelDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - changed = new ArrayList(); - } - - public static ModelDiff buildWithDefinition(Map left, - Map right) { - ModelDiff diff = new ModelDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; - return diff; - } - - public ModelDiff diff(Model leftModel, Model rightModel) { - return this.diff(leftModel, rightModel, null, new HashSet()); - } - - public ModelDiff diff(Model leftModel, Model rightModel, String parentEl) { - return this.diff(leftModel, rightModel, parentEl, new HashSet()); - } - - private ModelDiff diff(Model leftModel, Model rightModel, String parentEl, Set visited) { - // Stop recursing if both models are null - // OR either model is already contained in the visiting history - if ((null == leftModel && null == rightModel) || visited.contains(leftModel) || visited.contains(rightModel)) { - return this; - } - Map leftProperties = null == leftModel ? null : leftModel.getProperties(); - Map rightProperties = null == rightModel ? null : rightModel.getProperties(); - - // Diff the properties - MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); - - increased.addAll(convert2ElPropertys(propertyDiff.getIncreased(), parentEl)); - missing.addAll(convert2ElPropertys(propertyDiff.getMissing(), parentEl)); - - // Recursively find the diff between properties - List sharedKey = propertyDiff.getSharedKey(); - sharedKey.stream().forEach((key) -> { - Property left = leftProperties.get(key); - Property right = rightProperties.get(key); - - if ((left instanceof RefProperty) && (right instanceof RefProperty)) { - String leftRef = ((RefProperty) left).getSimpleRef(); - String rightRef = ((RefProperty) right).getSimpleRef(); - - diff(oldDedinitions.get(leftRef), newDedinitions.get(rightRef), - buildElString(parentEl, key), - copyAndAdd(visited, leftModel, rightModel)); - - } else if (left != null && right != null && !left.equals(right)) { - // Add a changed ElProperty if not a Reference - // Useless - changed.add(convert2ElProperty(key, parentEl, left)); - } - }); - return this; - } - - private Collection convert2ElPropertys( - Map propMap, String parentEl) { - - List result = new ArrayList(); - if (null == propMap) return result; - - for (Entry entry : propMap.entrySet()) { - // TODO Recursively get the properties - result.add(convert2ElProperty(entry.getKey(), parentEl, entry.getValue())); - } - return result; - } - - private String buildElString(String parentEl, String propName) { - return null == parentEl ? propName : (parentEl + "." + propName); - } - - private ElProperty convert2ElProperty(String propName, String parentEl, Property property) { - ElProperty pWithPath = new ElProperty(); - pWithPath.setProperty(property); - pWithPath.setEl(buildElString(parentEl, propName)); - return pWithPath; - } - - @SuppressWarnings("unchecked") + private ModelDiff() { + increased = new ArrayList(); + missing = new ArrayList(); + changed = new ArrayList(); + } + + public static ModelDiff buildWithDefinition(Map left, + Map right) { + ModelDiff diff = new ModelDiff(); + diff.oldDedinitions = left; + diff.newDedinitions = right; + return diff; + } + + public ModelDiff diff(Model leftModel, Model rightModel) { + return this.diff(leftModel, rightModel, null, new HashSet()); + } + + public ModelDiff diff(Model leftModel, Model rightModel, String parentEl) { + return this.diff(leftModel, rightModel, parentEl, new HashSet()); + } + + private ModelDiff diff(Model leftModel, Model rightModel, String parentEl, Set visited) { + // Stop recursing if both models are null + // OR either model is already contained in the visiting history + if ((null == leftModel && null == rightModel) || visited.contains(leftModel) || visited.contains(rightModel)) { + return this; + } + Map leftProperties = null == leftModel ? null : leftModel.getProperties(); + Map rightProperties = null == rightModel ? null : rightModel.getProperties(); + + // Diff the properties + MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties); + + increased.addAll(convert2ElPropertys(propertyDiff.getIncreased(), parentEl)); + missing.addAll(convert2ElPropertys(propertyDiff.getMissing(), parentEl)); + + // Recursively find the diff between properties + List sharedKey = propertyDiff.getSharedKey(); + sharedKey.stream().forEach((key) -> { + Property left = leftProperties.get(key); + Property right = rightProperties.get(key); + + if ((left instanceof RefProperty) && (right instanceof RefProperty)) { + String leftRef = ((RefProperty) left).getSimpleRef(); + String rightRef = ((RefProperty) right).getSimpleRef(); + + diff(oldDedinitions.get(leftRef), newDedinitions.get(rightRef), + buildElString(parentEl, key), + copyAndAdd(visited, leftModel, rightModel)); + + } else if (left != null && right != null && !left.equals(right)) { + // Add a changed ElProperty if not a Reference + // Useless + changed.add(convert2ElProperty(key, parentEl, left)); + } + }); + return this; + } + + private Collection convert2ElPropertys( + Map propMap, String parentEl) { + + List result = new ArrayList(); + if (null == propMap) return result; + + for (Entry entry : propMap.entrySet()) { + // TODO Recursively get the properties + result.add(convert2ElProperty(entry.getKey(), parentEl, entry.getValue())); + } + return result; + } + + private String buildElString(String parentEl, String propName) { + return null == parentEl ? propName : (parentEl + "." + propName); + } + + private ElProperty convert2ElProperty(String propName, String parentEl, Property property) { + ElProperty pWithPath = new ElProperty(); + pWithPath.setProperty(property); + pWithPath.setEl(buildElString(parentEl, propName)); + return pWithPath; + } + + @SuppressWarnings("unchecked") private Set copyAndAdd(Set set, T... add) { - Set newSet = new HashSet(set); - newSet.addAll(Arrays.asList(add)); - return newSet; - } + Set newSet = new HashSet(set); + newSet.addAll(Arrays.asList(add)); + return newSet; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java index 34abba3d..302db1e9 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java @@ -1,24 +1,20 @@ package com.deepoove.swagger.diff.compare; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.ClassUtils; -import org.apache.commons.lang3.StringUtils; - import com.deepoove.swagger.diff.model.ChangedParameter; - import io.swagger.models.Model; import io.swagger.models.RefModel; -import io.swagger.models.parameters.AbstractSerializableParameter; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.Parameter; import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * compare two parameter - * + * * @author Sayi */ @Data @@ -38,7 +34,7 @@ private ParameterDiff() { } public static ParameterDiff buildWithDefinition(Map left, - Map right) { + Map right) { ParameterDiff diff = new ParameterDiff(); diff.oldDedinitions = left; diff.newDedinitions = right; @@ -51,14 +47,16 @@ public ParameterDiff diff(List left, List right) { ListDiff paramDiff = ListDiff.diff(left, right, (t, param) -> { for (Parameter para : t) { - if (param.getName().equals(para.getName())) { return para; } + if (param.getName().equals(para.getName())) { + return para; + } } return null; }); this.increased.addAll(paramDiff.getIncreased()); this.missing.addAll(paramDiff.getMissing()); Map shared = paramDiff.getShared(); - + shared.forEach((leftPara, rightPara) -> { ChangedParameter changedParameter = new ChangedParameter(); changedParameter.setLeftParameter(leftPara); @@ -68,13 +66,13 @@ public ParameterDiff diff(List left, List right) { BodyParameter leftBodyPara = (BodyParameter) leftPara; Model leftSchema = leftBodyPara.getSchema(); BodyParameter rightBodyPara = (BodyParameter) rightPara; - Model rightSchema = rightBodyPara.getSchema(); + Model rightSchema = rightBodyPara.getSchema(); if (leftSchema instanceof RefModel && rightSchema instanceof RefModel) { String leftRef = ((RefModel) leftSchema).getSimpleRef(); String rightRef = ((RefModel) rightSchema).getSimpleRef(); Model leftModel = oldDedinitions.get(leftRef); Model rightModel = newDedinitions.get(rightRef); - + ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions) .diff(leftModel, rightModel, leftPara.getName()); changedParameter.setIncreased(diff.getIncreased()); diff --git a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java index c33aa7fa..cb6aec0d 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java @@ -1,52 +1,51 @@ package com.deepoove.swagger.diff.compare; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import com.deepoove.swagger.diff.model.ElProperty; - import io.swagger.models.Model; import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; import lombok.Data; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + @Data public class PropertyDiff { - private List increased; - private List missing; - private List changed; - - Map oldDedinitions; - Map newDedinitions; - - private PropertyDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - changed = new ArrayList(); - } - - public static PropertyDiff buildWithDefinition(Map left, - Map right) { - PropertyDiff diff = new PropertyDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; - return diff; - } - - public PropertyDiff diff(Property left, Property right) { - if ((null == left || left instanceof RefProperty) && (null == right || right instanceof RefProperty)) { - Model leftModel = null == left ? null : oldDedinitions.get(((RefProperty) left).getSimpleRef()); - Model rightModel = null == right ? null : newDedinitions.get(((RefProperty) right).getSimpleRef()); - ModelDiff diff = ModelDiff - .buildWithDefinition(oldDedinitions, newDedinitions) - .diff(leftModel, rightModel); - increased.addAll(diff.getIncreased()); - missing.addAll(diff.getMissing()); - changed.addAll(diff.getChanged()); - } - return this; - } + private List increased; + private List missing; + private List changed; + + Map oldDedinitions; + Map newDedinitions; + + private PropertyDiff() { + increased = new ArrayList(); + missing = new ArrayList(); + changed = new ArrayList(); + } + + public static PropertyDiff buildWithDefinition(Map left, + Map right) { + PropertyDiff diff = new PropertyDiff(); + diff.oldDedinitions = left; + diff.newDedinitions = right; + return diff; + } + + public PropertyDiff diff(Property left, Property right) { + if ((null == left || left instanceof RefProperty) && (null == right || right instanceof RefProperty)) { + Model leftModel = null == left ? null : oldDedinitions.get(((RefProperty) left).getSimpleRef()); + Model rightModel = null == right ? null : newDedinitions.get(((RefProperty) right).getSimpleRef()); + ModelDiff diff = ModelDiff + .buildWithDefinition(oldDedinitions, newDedinitions) + .diff(leftModel, rightModel); + increased.addAll(diff.getIncreased()); + missing.addAll(diff.getMissing()); + changed.addAll(diff.getChanged()); + } + return this; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java index 4540efba..aae9931e 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java @@ -1,28 +1,18 @@ package com.deepoove.swagger.diff.compare; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import com.deepoove.swagger.diff.model.ChangedEndpoint; import com.deepoove.swagger.diff.model.ChangedOperation; import com.deepoove.swagger.diff.model.Endpoint; - -import io.swagger.models.HttpMethod; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Response; -import io.swagger.models.Swagger; +import io.swagger.models.*; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; +import java.util.*; + /** * compare two Swagger - * - * @author Sayi * + * @author Sayi */ public class SpecificationDiff { @@ -30,11 +20,14 @@ public class SpecificationDiff { private List missingEndpoints; private List changedEndpoints; - private SpecificationDiff() {} + private SpecificationDiff() { + } public static SpecificationDiff diff(Swagger oldSpec, Swagger newSpec) { - if (null == oldSpec || null == newSpec) { throw new IllegalArgumentException( - "cannot diff null spec."); } + if (null == oldSpec || null == newSpec) { + throw new IllegalArgumentException( + "cannot diff null spec."); + } SpecificationDiff instance = new SpecificationDiff(); Map oldPaths = oldSpec.getPaths(); Map newPaths = newSpec.getPaths(); @@ -137,7 +130,7 @@ private static List convert2EndpointList(Map map) { } private static Collection convert2EndpointList(String pathUrl, - Map map) { + Map map) { List endpoints = new ArrayList(); if (null == map) return endpoints; map.forEach((httpMethod, operation) -> { diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java index 1b65c8a0..3220a775 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java @@ -1,11 +1,11 @@ package com.deepoove.swagger.diff.model; -import java.util.Map; - import io.swagger.models.HttpMethod; import io.swagger.models.Operation; -public class ChangedEndpoint implements Changed{ +import java.util.Map; + +public class ChangedEndpoint implements Changed { private String pathUrl; @@ -59,11 +59,11 @@ public boolean isDiff() { @Override public boolean isBackwardsCompatible() { - if(!missingOperations.isEmpty()) { + if (!missingOperations.isEmpty()) { return false; } else { for (ChangedOperation changedOperation : changedOperations.values()) { - if(!changedOperation.isBackwardsCompatible()) { + if (!changedOperation.isBackwardsCompatible()) { return false; } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java index 5e7ad00c..4fd11cb4 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java @@ -1,116 +1,116 @@ package com.deepoove.swagger.diff.model; -import java.util.ArrayList; -import java.util.List; - import io.swagger.models.parameters.Parameter; import lombok.Data; +import java.util.ArrayList; +import java.util.List; + @Data public class ChangedOperation implements Changed { - private String summary; + private String summary; - private List addParameters = new ArrayList(); - - private List missingParameters = new ArrayList(); - - private List changedParameters = new ArrayList(); - - private List addProps = new ArrayList(); - - private List missingProps = new ArrayList(); - - private List changedProps = new ArrayList(); - - public List getAddParameters() { - return addParameters; - } - - public void setAddParameters(List addParameters) { - this.addParameters = addParameters; - } - - public List getMissingParameters() { - return missingParameters; - } - - public void setMissingParameters(List missingParameters) { - this.missingParameters = missingParameters; - } - - public List getChangedParameters() { - return changedParameters; - } - - public void setChangedParameters(List changedParameters) { - this.changedParameters = changedParameters; - } - - public List getAddProps() { - return addProps; - } - - public void setAddProps(List addProps) { - this.addProps = addProps; - } - - public List getMissingProps() { - return missingProps; - } - - public void setMissingProps(List missingProps) { - this.missingProps = missingProps; - } - - public List getChangedProps() { - return changedProps; - } - - public void setChangedProps(List changedProps) { - this.changedProps = changedProps; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public boolean isDiff() { - return !addParameters.isEmpty() || !missingParameters.isEmpty() - || !changedParameters.isEmpty() || isDiffProp(); - } - - public boolean isDiffProp() { - return !addProps.isEmpty() - || !missingProps.isEmpty() - || !changedProps.isEmpty(); - } - - public boolean isDiffParam() { - return !addParameters.isEmpty() || !missingParameters.isEmpty() - || !changedParameters.isEmpty(); - } - - @Override - public boolean isBackwardsCompatible() { - if (!missingProps.isEmpty() || !missingParameters.isEmpty() || !changedProps.isEmpty()) { - return false; - } else { - for (ChangedParameter changedParameter : getChangedParameters()) { - if (!changedParameter.isBackwardsCompatible()) { - return false; - } - } - for (Parameter parameter : addParameters) { - if (parameter.getRequired()) { - return false; - } - } - } - return true; - } + private List addParameters = new ArrayList(); + + private List missingParameters = new ArrayList(); + + private List changedParameters = new ArrayList(); + + private List addProps = new ArrayList(); + + private List missingProps = new ArrayList(); + + private List changedProps = new ArrayList(); + + public List getAddParameters() { + return addParameters; + } + + public void setAddParameters(List addParameters) { + this.addParameters = addParameters; + } + + public List getMissingParameters() { + return missingParameters; + } + + public void setMissingParameters(List missingParameters) { + this.missingParameters = missingParameters; + } + + public List getChangedParameters() { + return changedParameters; + } + + public void setChangedParameters(List changedParameters) { + this.changedParameters = changedParameters; + } + + public List getAddProps() { + return addProps; + } + + public void setAddProps(List addProps) { + this.addProps = addProps; + } + + public List getMissingProps() { + return missingProps; + } + + public void setMissingProps(List missingProps) { + this.missingProps = missingProps; + } + + public List getChangedProps() { + return changedProps; + } + + public void setChangedProps(List changedProps) { + this.changedProps = changedProps; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public boolean isDiff() { + return !addParameters.isEmpty() || !missingParameters.isEmpty() + || !changedParameters.isEmpty() || isDiffProp(); + } + + public boolean isDiffProp() { + return !addProps.isEmpty() + || !missingProps.isEmpty() + || !changedProps.isEmpty(); + } + + public boolean isDiffParam() { + return !addParameters.isEmpty() || !missingParameters.isEmpty() + || !changedParameters.isEmpty(); + } + + @Override + public boolean isBackwardsCompatible() { + if (!missingProps.isEmpty() || !missingParameters.isEmpty() || !changedProps.isEmpty()) { + return false; + } else { + for (ChangedParameter changedParameter : getChangedParameters()) { + if (!changedParameter.isBackwardsCompatible()) { + return false; + } + } + for (Parameter parameter : addParameters) { + if (parameter.getRequired()) { + return false; + } + } + } + return true; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java index 4c1b34f3..f7305a2e 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java @@ -1,50 +1,50 @@ package com.deepoove.swagger.diff.model; -import java.util.ArrayList; -import java.util.List; - import io.swagger.models.parameters.Parameter; import lombok.Data; +import java.util.ArrayList; +import java.util.List; + @Data public class ChangedParameter implements Changed { - - private List increased = new ArrayList(); - private List missing = new ArrayList(); - private List changed = new ArrayList(); - private List typesChanges = new ArrayList(); - private List requiredChanges = new ArrayList(); - - private Parameter leftParameter; - private Parameter rightParameter; - - private boolean isChangeRequired; - private boolean isChangeDescription; - private boolean isChangeType; - - @Override - public boolean isDiff() { - return isChangeRequired - || isChangeDescription - || !increased.isEmpty() - || !missing.isEmpty() - || isChangeType - || !requiredChanges.isEmpty() - || !typesChanges.isEmpty(); - } - - @Override - public boolean isBackwardsCompatible() { - boolean isBackwardsCompatible = !isChangeRequired - && !isChangeType - && missing.isEmpty() - && requiredChanges.isEmpty() - && typesChanges.isEmpty(); - for (ElProperty elProperty : increased) { - if(elProperty.getProperty() != null && elProperty.getProperty().getRequired()) { - return false; - } - } - return isBackwardsCompatible; - } + + private List increased = new ArrayList(); + private List missing = new ArrayList(); + private List changed = new ArrayList(); + private List typesChanges = new ArrayList(); + private List requiredChanges = new ArrayList(); + + private Parameter leftParameter; + private Parameter rightParameter; + + private boolean isChangeRequired; + private boolean isChangeDescription; + private boolean isChangeType; + + @Override + public boolean isDiff() { + return isChangeRequired + || isChangeDescription + || !increased.isEmpty() + || !missing.isEmpty() + || isChangeType + || !requiredChanges.isEmpty() + || !typesChanges.isEmpty(); + } + + @Override + public boolean isBackwardsCompatible() { + boolean isBackwardsCompatible = !isChangeRequired + && !isChangeType + && missing.isEmpty() + && requiredChanges.isEmpty() + && typesChanges.isEmpty(); + for (ElProperty elProperty : increased) { + if (elProperty.getProperty() != null && elProperty.getProperty().getRequired()) { + return false; + } + } + return isBackwardsCompatible; + } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java index 56ba3f3c..2cfbafc1 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java @@ -5,8 +5,8 @@ /** * property with expression Language grammar + * * @author Sayi - * @version */ @Data public class ElProperty { diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index e3f63563..58993e7c 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -1,17 +1,7 @@ package com.deepoove.swagger.diff.output; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import com.deepoove.swagger.diff.SwaggerDiff; -import com.deepoove.swagger.diff.model.ChangedEndpoint; -import com.deepoove.swagger.diff.model.ChangedOperation; -import com.deepoove.swagger.diff.model.ChangedParameter; -import com.deepoove.swagger.diff.model.ElProperty; -import com.deepoove.swagger.diff.model.Endpoint; - +import com.deepoove.swagger.diff.model.*; import io.swagger.models.HttpMethod; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; @@ -19,6 +9,11 @@ import j2html.tags.DomContent; import j2html.tags.EmptyTag; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + import static j2html.TagCreator.*; public class HtmlRender implements Render { @@ -45,8 +40,8 @@ public HtmlRender(final String title, final String linkCss) { } /** - * @param pTitle : page's title - * @param pCssLinks : list of Css links + * @param pTitle : page's title + * @param pCssLinks : list of Css links * @param pScriptsJsLinks : list of JS Scripts links */ public HtmlRender(final String pTitle, final List pCssLinks, final List pScriptsJsLinks) { @@ -146,7 +141,7 @@ private ContainerTag ol_newEndpoint(final List endpoints) { } private ContainerTag li_newEndpoint(final String method, final String path, - final String desc) { + final String desc) { return li().with(span(method).withClass(method)).withText(path) .with(span(null == desc ? "" : " " + desc)); } @@ -164,7 +159,7 @@ private ContainerTag ol_missingEndpoint(final List endpoints) { } private ContainerTag li_missingEndpoint(final String method, final String path, - final String desc) { + final String desc) { return li().with(span(method).withClass(method), del().withText(path)).with(i_backwardsIncompatibilitiesWarning()).with(span(null == desc ? "" : " " + desc)); } @@ -328,7 +323,9 @@ private ContainerTag li_changedParam(final ChangedParameter changeParam) { return li; } - /** Add icon if modifications make backwards incompatibilies. */ + /** + * Add icon if modifications make backwards incompatibilies. + */ private ContainerTag i_backwardsIncompatibilitiesWarning() { return showBackwardsIncompatibilities ? i().withStyle("margin-left:0.5em;") .withClass("fas fa-exclamation-circle warnbackward").withTitle(NON_BACKWARDS_CHANGES) : null; diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index 0cbad1c1..7b15daf5 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -1,23 +1,14 @@ package com.deepoove.swagger.diff.output; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import com.deepoove.swagger.diff.SwaggerDiff; -import com.deepoove.swagger.diff.model.ChangedEndpoint; -import com.deepoove.swagger.diff.model.ChangedOperation; -import com.deepoove.swagger.diff.model.ChangedParameter; -import com.deepoove.swagger.diff.model.ElProperty; -import com.deepoove.swagger.diff.model.Endpoint; - +import com.deepoove.swagger.diff.model.*; import io.swagger.models.HttpMethod; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; -import j2html.tags.ContainerTag; -import sun.jvm.hotspot.oops.Mark; -import static j2html.TagCreator.*; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; public class MarkdownRender implements Render { @@ -58,7 +49,7 @@ public String render(SwaggerDiff diff) { } public String renderHtml(String oldVersion, String newVersion, String ol_new, String ol_miss, - String ol_changed) { + String ol_changed) { StringBuffer sb = new StringBuffer(); sb.append(H2).append("Version " + oldVersion + " to " + newVersion).append("\n").append(HR); sb.append(H3).append("What's New").append("\n").append(HR) @@ -280,7 +271,9 @@ private String li_changedParam(ChangedParameter changeParam) { return sb.toString(); } - /** Add icon if modifications make backwards incompatibilies. */ + /** + * Add icon if modifications make backwards incompatibilies. + */ private String i_backwardsIncompatibilitiesWarning() { return showBackwardsIncompatibilities ? " ❗ " : null; } diff --git a/src/main/java/com/deepoove/swagger/diff/output/Render.java b/src/main/java/com/deepoove/swagger/diff/output/Render.java index 674e1410..abc8dd78 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/Render.java +++ b/src/main/java/com/deepoove/swagger/diff/output/Render.java @@ -3,7 +3,7 @@ import com.deepoove.swagger.diff.SwaggerDiff; public interface Render { - - String render(SwaggerDiff diff); + + String render(SwaggerDiff diff); } diff --git a/src/test/java/com/deepoove/swagger/test/CLITest.java b/src/test/java/com/deepoove/swagger/test/CLITest.java index 358f3a97..3ad2a943 100644 --- a/src/test/java/com/deepoove/swagger/test/CLITest.java +++ b/src/test/java/com/deepoove/swagger/test/CLITest.java @@ -1,16 +1,15 @@ package com.deepoove.swagger.test; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import com.deepoove.swagger.diff.cli.CLI; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.ParameterException; -import com.deepoove.swagger.diff.cli.CLI; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; public class CLITest { @@ -29,8 +28,8 @@ public void restoreStreams() { @Test public void testCLI() { CLI cli = new CLI(); - String[] argv = { "-v", "2.0", "-old", "http://petstore.swagger.io/v2/swagger.json", - "--help" }; + String[] argv = {"-v", "2.0", "-old", "http://petstore.swagger.io/v2/swagger.json", + "--help"}; JCommander commander = JCommander.newBuilder().addObject(cli).build(); commander.setProgramName("java swagger-diff.jar"); @@ -43,13 +42,13 @@ public void testCLI() { @Test public void testRegex() { CLI cli = new CLI(); - String[] argv = { "--help", "-v", "2.0", "-output-mode", "markdown" }; + String[] argv = {"--help", "-v", "2.0", "-output-mode", "markdown"}; JCommander.newBuilder().addObject(cli).build().parse(argv); - argv = new String[] { "--help", "-v", "1.0", "-output-mode", "html" }; + argv = new String[]{"--help", "-v", "1.0", "-output-mode", "html"}; JCommander.newBuilder().addObject(cli).build().parse(argv); - argv = new String[] { "--help", "-v", "1.1.0" }; + argv = new String[]{"--help", "-v", "1.1.0"}; try { JCommander.newBuilder().addObject(cli).build().parse(argv); } catch (Exception e) { @@ -57,7 +56,7 @@ public void testRegex() { Assert.assertTrue(e instanceof ParameterException); } - argv = new String[] { "--help", "-output-mode", "html5" }; + argv = new String[]{"--help", "-output-mode", "html5"}; try { JCommander.newBuilder().addObject(cli).build().parse(argv); } catch (Exception e) { @@ -69,27 +68,27 @@ public void testRegex() { @Test public void testHelp() { CLI cli = new CLI(); - String[] argv = { "--help" }; + String[] argv = {"--help"}; JCommander jCommander = JCommander.newBuilder().addObject(cli).build(); jCommander.parse(argv); cli.run(jCommander); Assert.assertTrue(outContent.toString().startsWith("Usage: java -jar swagger-diff.jar ")); } - + @Test public void testVersion() { CLI cli = new CLI(); - String[] argv = { "--version" }; + String[] argv = {"--version"}; JCommander jCommander = JCommander.newBuilder().addObject(cli).build(); jCommander.parse(argv); cli.run(jCommander); Assert.assertEquals(outContent.toString().trim(), "1.2.1"); } - + @Test public void testMain() { CLI cli = new CLI(); - String[] argv = { "-old", "petstore_v2_1.json", "-new", "petstore_v2_2.json" }; + String[] argv = {"-old", "petstore_v2_1.json", "-new", "petstore_v2_2.json"}; JCommander jCommander = JCommander.newBuilder().addObject(cli).build(); jCommander.parse(argv); cli.run(jCommander); diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java index fe3a211b..b8263ae8 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffBackwardsIncompatibilitiesTest.java @@ -1,17 +1,16 @@ package com.deepoove.swagger.test; +import com.deepoove.swagger.diff.SwaggerDiff; +import com.deepoove.swagger.diff.model.ChangedEndpoint; +import com.deepoove.swagger.diff.output.HtmlRender; +import org.junit.Assert; +import org.junit.Test; + import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.junit.Assert; -import org.junit.Test; - -import com.deepoove.swagger.diff.SwaggerDiff; -import com.deepoove.swagger.diff.model.ChangedEndpoint; -import com.deepoove.swagger.diff.output.HtmlRender; - public class SwaggerDiffBackwardsIncompatibilitiesTest { private static final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java index cd71361c..2b11f8da 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java @@ -18,143 +18,143 @@ public class SwaggerDiffTest { - final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; - final String SWAGGER_V2_DOC2 = "petstore_v2_2.json"; - final String SWAGGER_V2_EMPTY_DOC = "petstore_v2_empty.json"; - final String SWAGGER_V2_HTTP = "http://petstore.swagger.io/v2/swagger.json"; - - @Test - public void testEqual() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC2, SWAGGER_V2_DOC2); - assertEqual(diff); - } - - @Test - public void testNewApi() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_EMPTY_DOC, SWAGGER_V2_DOC2); - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testNewApi.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertTrue(newEndpoints.size() > 0); - Assert.assertTrue(missingEndpoints.isEmpty()); - Assert.assertTrue(changedEndPoints.isEmpty()); - - } - - @Test - public void testDeprecatedApi() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_EMPTY_DOC); - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testDeprecatedApi.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertTrue(newEndpoints.isEmpty()); - Assert.assertTrue(missingEndpoints.size() > 0); - Assert.assertTrue(changedEndPoints.isEmpty()); - Assert.assertFalse("Contract must be incompatible.", diff.isBackwardsCompatible()); - - } - - @Test - public void testDiff() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - List changedEndPoints = diff.getChangedEndpoints(); - String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - - try { - FileWriter fw = new FileWriter( - "testDiff.html"); - fw.write(html); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - Assert.assertFalse(changedEndPoints.isEmpty()); - - } - - @Test - public void testDiffAndMarkdown() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - String render = new MarkdownRender().render(diff); - try { - FileWriter fw = new FileWriter( - "testDiff.md"); - fw.write(render); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - - } - - @Test - public void testEqualJson() { - try { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(SWAGGER_V2_DOC1); - JsonNode json = new ObjectMapper().readTree(inputStream); - SwaggerDiff diff = SwaggerDiff.compareV2(json, json); - assertEqual(diff); - } catch (IOException e) { - e.printStackTrace(); - } - - } - - - @Test - public void testJsonRender() { - SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - String render = new JsonRender().render(diff); - try { - FileWriter fw = new FileWriter( - "testDiff.json"); - fw.write(render); - fw.close(); - - } catch (IOException e) { - e.printStackTrace(); - } - } - - - private void assertEqual(SwaggerDiff diff) { - List newEndpoints = diff.getNewEndpoints(); - List missingEndpoints = diff.getMissingEndpoints(); - List changedEndPoints = diff.getChangedEndpoints(); - Assert.assertTrue(newEndpoints.isEmpty()); - Assert.assertTrue(missingEndpoints.isEmpty()); - Assert.assertTrue(changedEndPoints.isEmpty()); - - } + final String SWAGGER_V2_DOC1 = "petstore_v2_1.json"; + final String SWAGGER_V2_DOC2 = "petstore_v2_2.json"; + final String SWAGGER_V2_EMPTY_DOC = "petstore_v2_empty.json"; + final String SWAGGER_V2_HTTP = "http://petstore.swagger.io/v2/swagger.json"; + + @Test + public void testEqual() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC2, SWAGGER_V2_DOC2); + assertEqual(diff); + } + + @Test + public void testNewApi() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_EMPTY_DOC, SWAGGER_V2_DOC2); + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testNewApi.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertTrue(newEndpoints.size() > 0); + Assert.assertTrue(missingEndpoints.isEmpty()); + Assert.assertTrue(changedEndPoints.isEmpty()); + + } + + @Test + public void testDeprecatedApi() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_EMPTY_DOC); + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testDeprecatedApi.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertTrue(newEndpoints.isEmpty()); + Assert.assertTrue(missingEndpoints.size() > 0); + Assert.assertTrue(changedEndPoints.isEmpty()); + Assert.assertFalse("Contract must be incompatible.", diff.isBackwardsCompatible()); + + } + + @Test + public void testDiff() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); + List changedEndPoints = diff.getChangedEndpoints(); + String html = new HtmlRender("Changelog", + "http://deepoove.com/swagger-diff/stylesheets/demo.css") + .render(diff); + + try { + FileWriter fw = new FileWriter( + "testDiff.html"); + fw.write(html); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + Assert.assertFalse(changedEndPoints.isEmpty()); + + } + + @Test + public void testDiffAndMarkdown() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); + String render = new MarkdownRender().render(diff); + try { + FileWriter fw = new FileWriter( + "testDiff.md"); + fw.write(render); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Test + public void testEqualJson() { + try { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(SWAGGER_V2_DOC1); + JsonNode json = new ObjectMapper().readTree(inputStream); + SwaggerDiff diff = SwaggerDiff.compareV2(json, json); + assertEqual(diff); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + + @Test + public void testJsonRender() { + SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); + String render = new JsonRender().render(diff); + try { + FileWriter fw = new FileWriter( + "testDiff.json"); + fw.write(render); + fw.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + + private void assertEqual(SwaggerDiff diff) { + List newEndpoints = diff.getNewEndpoints(); + List missingEndpoints = diff.getMissingEndpoints(); + List changedEndPoints = diff.getChangedEndpoints(); + Assert.assertTrue(newEndpoints.isEmpty()); + Assert.assertTrue(missingEndpoints.isEmpty()); + Assert.assertTrue(changedEndPoints.isEmpty()); + + } } diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json index 2b09a1e9..c23a2ee9 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAddRequiredPropertyBody.json @@ -924,7 +924,7 @@ "required": [ "name", "photoUrls", - "num" + "num" ], "properties": { "id": { @@ -996,4 +996,4 @@ "description": "Find out more about Swagger", "url": "http://swagger.io" } -} \ No newline at end of file +} diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json index 3f646623..14b79c4f 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withAllBackwardsIncompatibilities.json @@ -875,8 +875,8 @@ "required": [ "name", "photoUrls", - "id", - "num" + "id", + "num" ], "properties": { "id": { @@ -945,4 +945,4 @@ "description": "Find out more about Swagger", "url": "http://swagger.io" } -} \ No newline at end of file +} diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json index dba27e60..41d19a24 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangePropertyBodyToRequired.json @@ -924,8 +924,8 @@ "required": [ "name", "photoUrls", - "category", - "id" + "category", + "id" ], "properties": { "id": { @@ -993,4 +993,4 @@ "description": "Find out more about Swagger", "url": "http://swagger.io" } -} \ No newline at end of file +} diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json index 6d5facba..c359df62 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json @@ -184,7 +184,6 @@ ] } }, - "/pet/findByStatus": { "get": { "tags": [ From cef0db14edbb3fbc36b3a2aa661fa9f135ab872c Mon Sep 17 00:00:00 2001 From: jlamaille Date: Wed, 1 Jul 2020 16:10:51 +0200 Subject: [PATCH 12/18] Fix merge & just 1 unit test failed --- .../swagger/diff/compare/ModelDiff.java | 29 +++--- .../swagger/diff/compare/ParameterDiff.java | 17 ++-- .../swagger/diff/compare/PropertyDiff.java | 6 +- .../swagger/diff/model/ChangedOperation.java | 3 +- .../swagger/diff/model/ChangedParameter.java | 10 ++- .../swagger/diff/model/ElProperty.java | 1 + .../swagger/diff/output/HtmlRender.java | 88 ++++--------------- .../petstore_v2_withChangesCompatibles.json | 3 + src/test/resources/petstore_v2_1.json | 2 +- 9 files changed, 56 insertions(+), 103 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java index 63ff9086..93cadaad 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java @@ -30,24 +30,22 @@ public class ModelDiff { private List increased; private List missing; private List changed; - private List requiredChanges; private List typeChanges; - Map oldDedinitions; - Map newDedinitions; + Map oldDefinitions; + Map newDefinitions; private ModelDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - changed = new ArrayList(); - requiredChanges = new ArrayList(); - typeChanges = new ArrayList(); + increased = new ArrayList<>(); + missing = new ArrayList<>(); + changed = new ArrayList<>(); + typeChanges = new ArrayList<>(); } public static ModelDiff buildWithDefinition(Map left, Map right) { ModelDiff diff = new ModelDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; + diff.oldDefinitions = left; + diff.newDefinitions = right; return diff; } @@ -60,7 +58,7 @@ public ModelDiff diff(Model leftModel, Model rightModel, String parentEl) { } public ModelDiff diff(Property leftProperty, Property rightProperty) { - return this.diff(findModel(leftProperty, oldDedinitions), findModel(rightProperty, newDedinitions)); + return this.diff(findModel(leftProperty, oldDefinitions), findModel(rightProperty, newDefinitions)); } private ModelDiff diff(Model leftInputModel, Model rightInputModel, String parentEl, Set visited) { @@ -70,9 +68,9 @@ private ModelDiff diff(Model leftInputModel, Model rightInputModel, String paren || visited.contains(rightInputModel)) { return this; } - Model leftModel = isModelReference(leftInputModel) ? findReferenceModel(leftInputModel, oldDedinitions) + Model leftModel = isModelReference(leftInputModel) ? findReferenceModel(leftInputModel, oldDefinitions) : leftInputModel; - Model rightModel = isModelReference(rightInputModel) ? findReferenceModel(rightInputModel, newDedinitions) + Model rightModel = isModelReference(rightInputModel) ? findReferenceModel(rightInputModel, newDefinitions) : rightInputModel; Map leftProperties = null == leftModel ? null : leftModel.getProperties(); Map rightProperties = null == rightModel ? null : rightModel.getProperties(); @@ -88,8 +86,8 @@ private ModelDiff diff(Model leftInputModel, Model rightInputModel, String paren sharedKey.stream().forEach((key) -> { Property left = leftProperties.get(key); Property right = rightProperties.get(key); - Model leftSubModel = findModel(left, oldDedinitions); - Model rightSubModel = findModel(left, newDedinitions); + Model leftSubModel = findModel(left, oldDefinitions); + Model rightSubModel = findModel(left, newDefinitions); if (leftSubModel != null || rightSubModel != null) { diff(leftSubModel, rightSubModel, buildElString(parentEl, key), copyAndAdd(visited, leftModel, rightModel)); @@ -127,6 +125,7 @@ private ElProperty convert2ElProperty(String propName, String parentEl, Property private ElProperty addChangeMetadata(ElProperty diffProperty, Property left, Property right) { diffProperty.setTypeChange(!left.getType().equalsIgnoreCase(right.getType())); + diffProperty.setBecomeRequired(left.getRequired() != right.getRequired()); List leftEnums = enumValues(left); List rightEnums = enumValues(right); if (!leftEnums.isEmpty() && !rightEnums.isEmpty()) { diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java index f78f918c..188bd9be 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java @@ -28,19 +28,19 @@ public class ParameterDiff { private List missing; private List changed; - Map oldDedinitions; - Map newDedinitions; + Map oldDefinitions; + Map newDefinitions; private ParameterDiff() { - this.increased = new ArrayList(); - this.missing = new ArrayList(); - this.changed = new ArrayList(); + this.increased = new ArrayList<>(); + this.missing = new ArrayList<>(); + this.changed = new ArrayList<>(); } public static ParameterDiff buildWithDefinition(Map left, Map right) { ParameterDiff diff = new ParameterDiff(); - diff.oldDedinitions = left; - diff.newDedinitions = right; + diff.oldDefinitions = left; + diff.newDefinitions = right; return diff; } @@ -69,11 +69,12 @@ public ParameterDiff diff(List left, List right) { BodyParameter rightBodyPara = (BodyParameter) rightPara; Model rightSchema = rightBodyPara.getSchema(); - ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions).diff(leftSchema, + ModelDiff diff = ModelDiff.buildWithDefinition(oldDefinitions, newDefinitions).diff(leftSchema, rightSchema, leftPara.getName()); changedParameter.setIncreased(diff.getIncreased()); changedParameter.setMissing(diff.getMissing()); changedParameter.setChanged(diff.getChanged()); + changedParameter.setTypesChanges(diff.getTypeChanges()); } diff --git a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java index 7a4dfa6c..493878f3 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java @@ -20,9 +20,9 @@ public class PropertyDiff { Map newDefinitions; private PropertyDiff() { - increased = new ArrayList(); - missing = new ArrayList(); - changed = new ArrayList(); + increased = new ArrayList<>(); + missing = new ArrayList<>(); + changed = new ArrayList<>(); } public static PropertyDiff buildWithDefinition(Map left, Map right) { diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java index 03d38dfc..ba07ba15 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java @@ -31,7 +31,8 @@ public boolean isDiff() { @Override public boolean isBackwardsCompatible() { - if (!missingProps.isEmpty() || !missingParameters.isEmpty() || !changedProps.isEmpty()) { + if (!missingProps.isEmpty() || !missingParameters.isEmpty() || !changedProps.isEmpty() + || isDiffConsumes() || isDiffProduces()) { return false; } else { for (ChangedParameter changedParameter : getChangedParameter()) { diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java index 548e378f..ad285fa9 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java @@ -13,7 +13,6 @@ public class ChangedParameter implements Changed { private List missing = new ArrayList<>(); private List changed = new ArrayList<>(); private List typesChanges = new ArrayList<>(); - private List requiredChanges = new ArrayList<>(); private Parameter leftParameter; private Parameter rightParameter; @@ -22,6 +21,11 @@ public class ChangedParameter implements Changed { private boolean isChangeDescription; private boolean isChangeType; +// public boolean isDiff() { +// return isChangeRequired || isChangeDescription || !increased.isEmpty() || !missing.isEmpty() +// || !changed.isEmpty(); +// } + @Override public boolean isDiff() { return isChangeRequired @@ -29,7 +33,6 @@ public boolean isDiff() { || !increased.isEmpty() || !missing.isEmpty() || isChangeType - || !requiredChanges.isEmpty() || !typesChanges.isEmpty(); } @@ -38,10 +41,9 @@ public boolean isBackwardsCompatible() { boolean isBackwardsCompatible = !isChangeRequired && !isChangeType && missing.isEmpty() - && requiredChanges.isEmpty() && typesChanges.isEmpty(); for (ElProperty elProperty : increased) { - if (elProperty.getProperty() != null && elProperty.getProperty().getRequired()) { + if (elProperty.isBecomeRequired() || elProperty.isNewEnums() || elProperty.isRemovedEnums()) { return false; } } diff --git a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java index c7b97c44..6b8fe72c 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ElProperty.java @@ -20,5 +20,6 @@ public class ElProperty { private boolean isTypeChange; private boolean newEnums; private boolean removedEnums; + private boolean isBecomeRequired; } diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index 7cb0b0f0..eca5b714 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -88,9 +88,6 @@ public String renderHtml(ContainerTag ol_new, ContainerTag ol_miss, ContainerTag articles.add(span().with(i_backwardsIncompatibilitiesWarning(), span().withText(" " + NON_BACKWARDS_CHANGES)).withStyle("float:right")); articles.add(br()); } - articles.add(div_headArticle("What's New", "new", ol_new)); - articles.add(div_headArticle("What's Missed", "missed", ol_miss)); - articles.add(div_headArticle("What's Changed", "changed", ol_changed)); ContainerTag html = html().attr("lang", "en").with( head().with( @@ -103,7 +100,7 @@ public String renderHtml(ContainerTag ol_new, ContainerTag ol_miss, ContainerTag div().withClass("article").with(articles).with( div_headArticle("Versions", "versions", p_versions), div_headArticle("What's New", "new", ol_new), - div_headArticle("What's Deprecated", "deprecated", ol_miss), + div_headArticle("What's Missing", "missing", ol_miss), div_headArticle("What's Changed", "changed", ol_changed) ) ), @@ -184,21 +181,21 @@ private ContainerTag ol_changed(final List changedEndpoints) { if (changedOperation.isDiffProp()) { ul_detail.with(li().with(h3("Return Type")).with(ul_response(changedOperation))); } - ContainerTag li = li(); - li.with(span(method).withClass(method)).withText(pathUrl); - if (!changedEndpoint.isBackwardsCompatible()) { - li.with(i_backwardsIncompatibilitiesWarning()); - } - li.with(span(null == desc ? "" : " " + desc)).with(ul_detail); - ol.with(li); if (changedOperation.isDiffProduces()) { ul_detail.with(li().with(h3("Produces")).with(ul_produce(changedOperation))); } if (changedOperation.isDiffConsumes()) { ul_detail.with(li().with(h3("Consumes")).with(ul_consume(changedOperation))); } - ol.with(li().with(span(method).withClass(method)).withText(pathUrl + " ").with(span(null == desc ? "" : desc)) - .with(ul_detail)); + ContainerTag li = li(); + li.with(span(method).withClass(method)).withText(pathUrl + " "); + + if (!changedEndpoint.isBackwardsCompatible()) { + li.with(i_backwardsIncompatibilitiesWarning()); + } + li.with(span(null == desc ? "" : desc)); + li.with(ul_detail); + ol.with(li); } } return ol; @@ -212,9 +209,6 @@ private ContainerTag ul_response(final ChangedOperation changedOperation) { for (ElProperty prop : addProps) { ul.with(li_addProp(prop)); } - for (ElProperty prop : chgProps) { - ul.with(li_changeTypeProp(prop)); - } for (ElProperty prop : delProps) { ul.with(li_missingProp(prop)); } @@ -238,20 +232,10 @@ private ContainerTag li_addProp(final ElProperty prop) { return li.with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); } - private ContainerTag li_changeTypeProp(final ElProperty prop) { - Property property = prop.getProperty(); - return li().with(textField(prop.getEl())).withText(" changes type").with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); - } - private ContainerTag textField(final String pField) { return span().withText(pField).withClass("field"); } - private ContainerTag li_changeRequiredProp(final ElProperty prop) { - Property property = prop.getProperty(); - return li().with(textField(prop.getEl())).withText(" change into required").with(i_backwardsIncompatibilitiesWarning()).with(span(null == property.getDescription() ? "" : ("//" + property.getDescription())).withClass("comment")); - } - private ContainerTag li_changedProp(ElProperty prop) { List changeDetails = new ArrayList<>(); String changeDetailsHeading = ""; @@ -264,10 +248,14 @@ private ContainerTag li_changedProp(ElProperty prop) { if (prop.isRemovedEnums()) { changeDetails.add("Removed Enum"); } + if(prop.isBecomeRequired()) { + changeDetails.add("Becomes required"); + } if (!changeDetails.isEmpty()) { - changeDetailsHeading = " (" + String.join(", ", changeDetails) + ")"; + changeDetailsHeading = " (" + String.join(", ", changeDetails) + ")" ; } - return li().withText("Change " + prop.getEl()).with(span(changeDetailsHeading).withClass("comment")); + return li().withText("Change " + prop.getEl()).with(span(changeDetailsHeading).withClass("comment")) + .with(i_backwardsIncompatibilitiesWarning()); } private ContainerTag ul_param(ChangedOperation changedOperation) { @@ -284,23 +272,6 @@ private ContainerTag ul_param(ChangedOperation changedOperation) { ul.with(li_addProp(prop)); } } - for (ChangedParameter param : changedParameters) { - List requiredChanges = param.getRequiredChanges(); - for (ElProperty prop : requiredChanges) { - ul.with(li_changeRequiredProp(prop)); - } - } - for (ChangedParameter param : changedParameters) { - List typesChanges = param.getTypesChanges(); - for (ElProperty prop : typesChanges) { - ul.with(li_changeTypeProp(prop)); - } - } - for (ChangedParameter param : changedParameters) { - if (param.isChangeRequired() || param.isChangeDescription() || param.isChangeType()) { - ul.with(li_changedParam(param)); - } - } for (ChangedParameter param : changedParameters) { List missing = param.getMissing(); for (ElProperty prop : missing) { @@ -333,36 +304,11 @@ private ContainerTag li_missingParam(final Parameter param) { return li().withClass("missing").with(span("Delete")).with(del(textField(param.getName()))).with(i_backwardsIncompatibilitiesWarning()).with(span(null == param.getDescription() ? "" : ("//" + param.getDescription())).withClass("comment")); } - private ContainerTag li_changedParam(final ChangedParameter changeParam) { - boolean changeRequired = changeParam.isChangeRequired(); - boolean changeDescription = changeParam.isChangeDescription(); - boolean changeType = changeParam.isChangeType(); - Parameter rightParam = changeParam.getRightParameter(); - Parameter leftParam = changeParam.getLeftParameter(); - ContainerTag li = li().with(textField(rightParam.getName())); - if (changeRequired || changeType || changeDescription) { - li.withText(" : "); - if (changeRequired) { - li.with(br()).withText(" - becomes " + (rightParam.getRequired() ? "required" : "not required")); - } - if (changeType) { - li.with(br()).withText(" - changes type"); - } - if (changeDescription) { - li.with(br()).withText(" - notes ").with(del(leftParam.getDescription()).withClass("comment")).withText(" change into ").with(span(span(null == rightParam.getDescription() ? "" : rightParam.getDescription()).withClass("comment"))); - } - } - if (!changeParam.isBackwardsCompatible()) { - li.with(i_backwardsIncompatibilitiesWarning()); - } - return li; - } - /** * Add icon if modifications make backwards incompatibilies. */ private ContainerTag i_backwardsIncompatibilitiesWarning() { - return showBackwardsIncompatibilities ? i().withStyle("margin-left:0.5em;") + return showBackwardsIncompatibilities ? i().withStyle("margin-left:0.5em;margin-right:0.5em;") .withClass("fas fa-exclamation-circle warnbackward").withTitle(NON_BACKWARDS_CHANGES) : null; } diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json index c359df62..a6d1bb77 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json @@ -982,6 +982,9 @@ }, "name": { "type": "string" + }, + "removedField": { + "type": "string" } }, "xml": { diff --git a/src/test/resources/petstore_v2_1.json b/src/test/resources/petstore_v2_1.json index 4d4e6b40..6d9f3761 100644 --- a/src/test/resources/petstore_v2_1.json +++ b/src/test/resources/petstore_v2_1.json @@ -1006,4 +1006,4 @@ "description": "Find out more about Swagger", "url": "http://swagger.io" } -} \ No newline at end of file +} From ee24ed84ff683316475ccc6b0332a29371c09798 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Thu, 2 Jul 2020 11:01:16 +0200 Subject: [PATCH 13/18] Fix parameter diff & syntax --- .../swagger/diff/compare/ParameterDiff.java | 6 +- .../swagger/diff/model/ChangedParameter.java | 3 +- .../swagger/diff/output/MarkdownRender.java | 10 +- .../swagger/test/SwaggerDiffTest.java | 4 +- .../petstore_v2_withChangesCompatibles.json | 3 + swagger-diff.ipr | 107 ----- swagger-diff.iws | 418 ------------------ 7 files changed, 19 insertions(+), 532 deletions(-) delete mode 100644 swagger-diff.ipr delete mode 100644 swagger-diff.iws diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java index 188bd9be..efc82c9b 100644 --- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java +++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java @@ -97,10 +97,10 @@ public ParameterDiff diff(List left, List right) { // description String description = rightPara.getDescription(); - String oldPescription = leftPara.getDescription(); + String oldDescription = leftPara.getDescription(); if (StringUtils.isBlank(description)) description = ""; - if (StringUtils.isBlank(oldPescription)) oldPescription = ""; - changedParameter.setChangeDescription(!description.equals(oldPescription)); + if (StringUtils.isBlank(oldDescription)) oldDescription = ""; + changedParameter.setChangeDescription(!description.equals(oldDescription)); if (changedParameter.isDiff()) { this.changed.add(changedParameter); diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java index ad285fa9..d35f7f2d 100644 --- a/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java +++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedParameter.java @@ -33,7 +33,8 @@ public boolean isDiff() { || !increased.isEmpty() || !missing.isEmpty() || isChangeType - || !typesChanges.isEmpty(); + || !typesChanges.isEmpty() + || !changed.isEmpty(); } @Override diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index 4e825fc7..d7281fc6 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -5,6 +5,7 @@ import io.swagger.models.HttpMethod; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.Property; +import org.apache.commons.lang3.StringUtils; import java.util.List; import java.util.Map; @@ -25,6 +26,11 @@ public class MarkdownRender implements Render { public MarkdownRender() { } + public MarkdownRender withBackwardsIncompatibilities() { + showBackwardsIncompatibilities = true; + return this; + } + public String render(SwaggerDiff diff) { List newEndpoints = diff.getNewEndpoints(); String ol_newEndpoint = ol_newEndpoint(newEndpoints); @@ -44,7 +50,7 @@ public String renderHtml(String oldVersion, String newVersion, String ol_new, St sb.append(H2).append("Version " + oldVersion + " to " + newVersion).append("\n").append(HR); sb.append(H3).append("What's New").append("\n").append(HR) .append(ol_new).append("\n").append(H3) - .append("What's Deprecated").append("\n").append(HR) + .append("What's Missing").append("\n").append(HR) .append(ol_miss).append("\n").append(H3) .append("What's Changed").append("\n").append(HR) .append(ol_changed); @@ -305,6 +311,6 @@ private String li_addMediaType(String type) { * Add icon if modifications make backwards incompatibilies. */ private String i_backwardsIncompatibilitiesWarning() { - return showBackwardsIncompatibilities ? " ❗ " : null; + return showBackwardsIncompatibilities ? " ❗ " : StringUtils.EMPTY; } } diff --git a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java index ead20b3e..bc44c75a 100644 --- a/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java +++ b/src/test/java/com/deepoove/swagger/test/SwaggerDiffTest.java @@ -130,7 +130,9 @@ public void testDiff() { @Test public void testDiffAndMarkdown() { SwaggerDiff diff = SwaggerDiff.compareV2(SWAGGER_V2_DOC1, SWAGGER_V2_DOC2); - String render = new MarkdownRender().render(diff); + String render = new MarkdownRender() + .withBackwardsIncompatibilities() + .render(diff); try { FileWriter fw = new FileWriter( "testDiff.md"); diff --git a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json index a6d1bb77..dee9114f 100644 --- a/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json +++ b/src/test/resources/backwards_incompatibilities/petstore_v2_withChangesCompatibles.json @@ -493,6 +493,9 @@ "summary": "Place an order for a pet", "description": "", "operationId": "placeOrder", + "consumes": [ + "application/xml" + ], "produces": [ "application/xml", "application/json" diff --git a/swagger-diff.ipr b/swagger-diff.ipr deleted file mode 100644 index 1200dbce..00000000 --- a/swagger-diff.ipr +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/swagger-diff.iws b/swagger-diff.iws deleted file mode 100644 index 57de9a0c..00000000 --- a/swagger-diff.iws +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 5ae410de696a4a6cabd6ed2a13e97c05d7d9c6da Mon Sep 17 00:00:00 2001 From: jlamaille Date: Thu, 2 Jul 2020 13:42:27 +0200 Subject: [PATCH 14/18] Add icon for produce and consume changes --- src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java | 2 +- .../java/com/deepoove/swagger/diff/output/MarkdownRender.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java index eca5b714..620e06f6 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/HtmlRender.java @@ -339,7 +339,7 @@ private ContainerTag ul_consume(ChangedOperation changedOperation) { } private ContainerTag li_missingMediaType(String type) { - return li().withClass("missing").withText("Delete").with(del(type)).with(span("")); + return li().withClass("missing").withText("Delete").with(del(type)).with(span("")).with(i_backwardsIncompatibilitiesWarning()); } private ContainerTag li_addMediaType(String type) { diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index d7281fc6..dbf8c46a 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -303,7 +303,7 @@ private String li_missingMediaType(String type) { private String li_addMediaType(String type) { StringBuffer sb = new StringBuffer(""); - sb.append("Insert ").append(type); + sb.append("Insert ").append(type).append(i_backwardsIncompatibilitiesWarning()); return sb.toString(); } From 88d813008c7b65087aa0e54f16b6978163a53c48 Mon Sep 17 00:00:00 2001 From: jlamaille Date: Thu, 2 Jul 2020 13:43:30 +0200 Subject: [PATCH 15/18] Fix icon --- .../java/com/deepoove/swagger/diff/output/MarkdownRender.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java index dbf8c46a..72e37341 100644 --- a/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java +++ b/src/main/java/com/deepoove/swagger/diff/output/MarkdownRender.java @@ -297,13 +297,13 @@ private String ul_consume(ChangedOperation changedOperation) { private String li_missingMediaType(String type) { StringBuffer sb = new StringBuffer(""); - sb.append("Delete ").append(type); + sb.append("Delete ").append(type).append(i_backwardsIncompatibilitiesWarning()); return sb.toString(); } private String li_addMediaType(String type) { StringBuffer sb = new StringBuffer(""); - sb.append("Insert ").append(type).append(i_backwardsIncompatibilitiesWarning()); + sb.append("Insert ").append(type); return sb.toString(); } From d85d8c82ff501bc228e1530628ca891ccb9ff27b Mon Sep 17 00:00:00 2001 From: jlamaille Date: Thu, 2 Jul 2020 13:46:23 +0200 Subject: [PATCH 16/18] Delete useless files --- testDiff.json | 1 - testDiff.md | 71 --------------------------------------------------- 2 files changed, 72 deletions(-) delete mode 100644 testDiff.json delete mode 100644 testDiff.md diff --git a/testDiff.json b/testDiff.json deleted file mode 100644 index 4a047946..00000000 --- a/testDiff.json +++ /dev/null @@ -1 +0,0 @@ -{"backwardsCompatible":false,"changedEndpoints":[{"backwardsCompatible":false,"changedOperations":{"PUT":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.name","newEnums":false,"property":{"example":"doggie","required":true,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newFeild","newEnums":false,"property":{"description":"a feild demo by sayi","example":"a feild demo by sayi","required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false},{"el":"body.category.newCatFeild","newEnums":false,"property":{"required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false},{"el":"body.owner.newUserFeild","newEnums":false,"property":{"description":"a new user feild demo","format":"int32","required":false,"type":"integer","vendorExtensions":{}},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"Pet object that needs to be added to the store","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Pet","simpleRef":"Pet"},"vendorExtensions":{}},"missing":[{"el":"body.category.name","newEnums":false,"property":{"required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false},{"el":"body.owner.phone","newEnums":false,"property":{"required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false},{"el":"body.tags.removedField","newEnums":false,"property":{"required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"Pet object that needs to be added to the store","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Pet","simpleRef":"Pet"},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Update an existing pet"},"POST":{"addConsumes":[],"addParameters":[{"collectionFormat":"multi","description":"add new query param demo","in":"query","items":{"required":false,"type":"string","vendorExtensions":{}},"name":"tags","required":true,"type":"array","vendorExtensions":{}}],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"Pet object that needs to be added to the store","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Pet","simpleRef":"Pet"},"vendorExtensions":{}},"missing":[{"el":"body.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"Pet object that needs to be added to the store","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Pet","simpleRef":"Pet"},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Add a new pet to the store"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet"},{"backwardsCompatible":false,"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false}],"backwardsCompatible":false,"changedParameter":[],"changedProps":[{"el":"name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"summary":"Finds Pets by status"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/findByStatus"},{"backwardsCompatible":false,"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false}],"backwardsCompatible":false,"changedParameter":[],"changedProps":[{"el":"name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"summary":"Finds Pets by tags"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/findByTags"},{"backwardsCompatible":false,"changedOperations":{"DELETE":{"addConsumes":[],"addParameters":[{"in":"header","name":"newHeaderParam","required":false,"type":"string","vendorExtensions":{}}],"addProduces":[],"addProps":[],"backwardsCompatible":true,"changedParameter":[],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Deletes a pet"}},"diff":true,"missingOperations":{"POST":{"consumes":["application/x-www-form-urlencoded"],"description":"","operationId":"updatePetWithForm","parameters":[{"description":"ID of pet that needs to be updated","format":"int64","in":"path","name":"petId","required":true,"type":"integer","vendorExtensions":{}},{"description":"Updated name of the pet","in":"formData","name":"name","required":false,"type":"string","vendorExtensions":{}},{"description":"Updated status of the pet","in":"formData","name":"status","required":false,"type":"string","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"405":{"description":"Invalid input","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Updates a pet in the store with form data","tags":["pet"],"vendorExtensions":{}}},"newOperations":{"GET":{"description":"Returns a single pet","operationId":"getPetById","parameters":[{"description":"ID of pet to return","format":"int64","in":"path","name":"petId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Pet not found","vendorExtensions":{}}},"security":[{"api_key":[]}],"summary":"Find pet by ID","tags":["pet"],"vendorExtensions":{}}},"pathUrl":"/pet/{petId}"},{"backwardsCompatible":false,"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":true,"changeRequired":true,"changeType":false,"changed":[{"el":"petId","newEnums":false,"property":{"description":"ID of pet to update, default false","name":"petId","required":false,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[],"leftParameter":{"description":"ID of pet to update","format":"int64","in":"path","name":"petId","required":true,"type":"integer","vendorExtensions":{}},"missing":[],"requiredChanges":[],"rightParameter":{"description":"ID of pet to update, default false","format":"int64","in":"path","name":"petId","required":false,"type":"integer","vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"uploads an image for pet"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/{petId}/uploadImage"},{"backwardsCompatible":false,"changedOperations":{"POST":{"addConsumes":["application/json"],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[],"changedProps":[{"el":"id","newEnums":false,"property":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"description":"Order Status","enum":["placed","approved","delivered"],"required":false,"type":"string","vendorExtensions":{}},"removedEnums":true,"typeChange":false}],"diff":true,"diffConsumes":true,"diffParam":false,"diffProduces":true,"diffProp":true,"missingConsumes":["application/xml"],"missingParameters":[],"missingProduces":["application/xml"],"missingProps":[],"summary":"Place an order for a pet"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/store/order"},{"backwardsCompatible":false,"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[],"changedProps":[{"el":"id","newEnums":false,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"removedEnums":true,"typeChange":false}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Find purchase order by ID"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/store/order/{orderId}"},{"backwardsCompatible":false,"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.favorite.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"Created user object","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/User","simpleRef":"User"},"vendorExtensions":{}},"missing":[{"el":"body.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"Created user object","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/User","simpleRef":"User"},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Create user"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user"},{"backwardsCompatible":false,"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.favorite.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"List of user object","in":"body","name":"body","required":true,"schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"missing":[{"el":"body.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"List of user object","in":"body","name":"body","required":true,"schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Creates list of users with given input array"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/createWithArray"},{"backwardsCompatible":false,"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.favorite.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"List of user object","in":"body","name":"body","required":true,"schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"missing":[{"el":"body.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"List of user object","in":"body","name":"body","required":true,"schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Creates list of users with given input array"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/createWithList"},{"backwardsCompatible":false,"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[{"description":"The password for login in clear text","in":"query","name":"password","required":true,"type":"string","vendorExtensions":{}}],"missingProduces":[],"missingProps":[],"summary":"Logs user into the system"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/login"},{"backwardsCompatible":false,"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false}],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":true,"changeDescription":true,"changeRequired":false,"changeType":false,"changed":[{"el":"username","newEnums":false,"property":{"description":"The name that needs to be fetched. Use user1 for testing. edited","name":"username","required":true,"type":"string","vendorExtensions":{}},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[],"leftParameter":{"description":"The name that needs to be fetched. Use user1 for testing. ","in":"path","name":"username","required":true,"type":"string","vendorExtensions":{}},"missing":[],"requiredChanges":[],"rightParameter":{"description":"The name that needs to be fetched. Use user1 for testing. edited","format":"int32","in":"path","name":"username","required":true,"type":"integer","vendorExtensions":{}},"typesChanges":[]}],"changedProps":[{"el":"favorite.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"summary":"Get user by user name"},"PUT":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"backwardsCompatible":false,"changedParameter":[{"backwardsCompatible":false,"changeDescription":false,"changeRequired":false,"changeType":false,"changed":[{"el":"body.favorite.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[{"el":"body.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"},"removedEnums":false,"typeChange":false}],"leftParameter":{"description":"Updated user object","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/User","simpleRef":"User"},"vendorExtensions":{}},"missing":[{"el":"body.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"},"removedEnums":false,"typeChange":false},{"el":"body.favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"},"removedEnums":false,"typeChange":false}],"requiredChanges":[],"rightParameter":{"description":"Updated user object","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/User","simpleRef":"User"},"vendorExtensions":{}},"typesChanges":[]}],"changedProps":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"summary":"Updated user"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/{username}"}],"missingEndpoints":[{"method":"POST","operation":{"$ref":"$.changedEndpoints[3].missingOperations.POST"},"pathUrl":"/pet/{petId}","summary":"Updates a pet in the store with form data"}],"newEndpoints":[{"method":"GET","operation":{"$ref":"$.changedEndpoints[3].newOperations.GET"},"pathUrl":"/pet/{petId}","summary":"Find pet by ID"}],"newSpecSwagger":{"basePath":"/v2","definitions":{"Order":{"properties":{"id":{"required":false,"type":"string","vendorExtensions":{}},"petId":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"quantity":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"shipDate":{"format":"date-time","required":false,"type":"string","vendorExtensions":{}},"status":{"description":"Order Status","enum":["placed","authorized","delivered"],"required":false,"type":"string","vendorExtensions":{}},"complete":{"default":false,"required":false,"type":"boolean","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Order"}},"Category":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"newCatFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[1].property"}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Category"}},"User":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"username":{"required":false,"type":"string","vendorExtensions":{}},"firstName":{"required":false,"type":"string","vendorExtensions":{}},"lastName":{"required":false,"type":"string","vendorExtensions":{}},"email":{"required":false,"type":"string","vendorExtensions":{}},"password":{"required":false,"type":"string","vendorExtensions":{}},"favorite":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"userStatus":{"description":"User Status","format":"int32","required":false,"type":"integer","vendorExtensions":{}},"newUserFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[2].property"}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"User"}},"Tag":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"name":{"required":false,"type":"string","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Tag"}},"Pet":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"category":{"refFormat":"INTERNAL","required":false,"simpleRef":"Category","type":"ref","vendorExtensions":{}},"name":{"example":"doggies","required":true,"type":"string","vendorExtensions":{}},"newFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].increased[0].property"},"owner":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"parent":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"photoUrls":{"items":{"required":false,"type":"string","vendorExtensions":{}},"required":true,"type":"array","vendorExtensions":{},"xml":{"name":"photoUrl","wrapped":true}},"tags":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Tag","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{},"xml":{"name":"tag","wrapped":true}},"status":{"description":"pet status in the store","enum":["available","pending","sold"],"required":false,"type":"string","vendorExtensions":{}}},"required":["name","photoUrls"],"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Pet"}},"ApiResponse":{"properties":{"code":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"type":{"required":false,"type":"string","vendorExtensions":{}},"message":{"required":false,"type":"string","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{}}},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io","vendorExtensions":{}},"host":"petstore.swagger.io","info":{"contact":{"email":"apiteam@swagger.io"},"description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html","vendorExtensions":{}},"termsOfService":"http://swagger.io/terms/","title":"Swagger Petstore","vendorExtensions":{},"version":"1.0.2"},"paths":{"/pet":{"empty":false,"operationMap":{"PUT":{"consumes":["application/json","application/xml"],"description":"","operationId":"updatePet","parameters":[{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Pet not found","vendorExtensions":{}},"405":{"description":"Validation exception","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Update an existing pet","tags":["pet"],"vendorExtensions":{}},"POST":{"consumes":["application/json","application/xml"],"description":"","operationId":"addPet","parameters":[{"$ref":"$.changedEndpoints[0].changedOperations.POST.changedParameter[0].rightParameter"},{"$ref":"$.changedEndpoints[0].changedOperations.POST.addParameters[0]"}],"produces":["application/xml","application/json"],"responses":{"405":{"description":"Invalid input","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Add a new pet to the store","tags":["pet"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./pet.operationMap.PUT"},{"$ref":"$.newSpecSwagger.paths./pet.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./pet.operationMap.POST"},"put":{"$ref":"$.newSpecSwagger.paths./pet.operationMap.PUT"},"vendorExtensions":{}},"/pet/findByStatus":{"empty":false,"get":{"description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","parameters":[{"collectionFormat":"multi","description":"Status values that need to be considered for filter","in":"query","items":{"default":"available","enum":["available","pending","sold"],"required":false,"type":"string","vendorExtensions":{}},"name":"status","required":true,"type":"array","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid status value","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Finds Pets by status","tags":["pet"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./pet/findByStatus.get"}},"operations":[{"$ref":"$.newSpecSwagger.paths./pet/findByStatus.get"}],"vendorExtensions":{}},"/pet/findByTags":{"empty":false,"get":{"deprecated":true,"description":"Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"collectionFormat":"multi","description":"Tags to filter by","in":"query","items":{"required":false,"type":"string","vendorExtensions":{}},"name":"tags","required":true,"type":"array","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid tag value","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Finds Pets by tags","tags":["pet"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./pet/findByTags.get"}},"operations":[{"$ref":"$.newSpecSwagger.paths./pet/findByTags.get"}],"vendorExtensions":{}},"/pet/{petId}":{"delete":{"description":"","operationId":"deletePet","parameters":[{"in":"header","name":"api_key","required":false,"type":"string","vendorExtensions":{}},{"$ref":"$.changedEndpoints[3].changedOperations.DELETE.addParameters[0]"},{"description":"Pet id to delete","format":"int64","in":"path","name":"petId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Pet not found","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Deletes a pet","tags":["pet"],"vendorExtensions":{}},"empty":false,"get":{"$ref":"$.changedEndpoints[3].newOperations.GET"},"operationMap":{"GET":{"$ref":"$.changedEndpoints[3].newOperations.GET"},"DELETE":{"$ref":"$.newSpecSwagger.paths./pet/{petId}.delete"}},"operations":[{"$ref":"$.changedEndpoints[3].newOperations.GET"},{"$ref":"$.newSpecSwagger.paths./pet/{petId}.delete"}],"vendorExtensions":{}},"/pet/{petId}/uploadImage":{"empty":false,"operationMap":{"POST":{"consumes":["multipart/form-data"],"description":"","operationId":"uploadFile","parameters":[{"$ref":"$.changedEndpoints[4].changedOperations.POST.changedParameter[0].rightParameter"},{"description":"Additional data to pass to server","in":"formData","name":"additionalMetadata","required":false,"type":"string","vendorExtensions":{}},{"description":"file to upload","in":"formData","name":"file","required":false,"type":"file","vendorExtensions":{}}],"produces":["application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"ApiResponse","type":"ref","vendorExtensions":{}},"vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"uploads an image for pet","tags":["pet"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./pet/{petId}/uploadImage.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./pet/{petId}/uploadImage.operationMap.POST"},"vendorExtensions":{}},"/store/inventory":{"empty":false,"get":{"description":"Returns a map of status codes to quantities","operationId":"getInventory","parameters":[],"produces":["application/json"],"responses":{"200":{"description":"successful operation","schema":{"additionalProperties":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"required":false,"type":"object","vendorExtensions":{}},"vendorExtensions":{}}},"security":[{"api_key":[]}],"summary":"Returns pet inventories by status","tags":["store"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./store/inventory.get"}},"operations":[{"$ref":"$.newSpecSwagger.paths./store/inventory.get"}],"vendorExtensions":{}},"/store/order":{"empty":false,"operationMap":{"POST":{"consumes":["application/json"],"description":"","operationId":"placeOrder","parameters":[{"description":"order placed for purchasing the pet","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Order","simpleRef":"Order"},"vendorExtensions":{}}],"produces":["application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"Order","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid Order","vendorExtensions":{}}},"summary":"Place an order for a pet","tags":["store"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./store/order.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./store/order.operationMap.POST"},"vendorExtensions":{}},"/store/order/{orderId}":{"delete":{"description":"For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors","operationId":"deleteOrder","parameters":[{"description":"ID of the order that needs to be deleted","format":"int64","in":"path","minimum":1,"name":"orderId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Order not found","vendorExtensions":{}}},"summary":"Delete purchase order by ID","tags":["store"],"vendorExtensions":{}},"empty":false,"get":{"description":"For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions","operationId":"getOrderById","parameters":[{"description":"ID of pet that needs to be fetched","format":"int64","in":"path","maximum":10,"minimum":1,"name":"orderId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"Order","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Order not found","vendorExtensions":{}}},"summary":"Find purchase order by ID","tags":["store"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./store/order/{orderId}.get"},"DELETE":{"$ref":"$.newSpecSwagger.paths./store/order/{orderId}.delete"}},"operations":[{"$ref":"$.newSpecSwagger.paths./store/order/{orderId}.get"},{"$ref":"$.newSpecSwagger.paths./store/order/{orderId}.delete"}],"vendorExtensions":{}},"/user":{"empty":false,"operationMap":{"POST":{"description":"This can only be done by the logged in user.","operationId":"createUser","parameters":[{"$ref":"$.changedEndpoints[7].changedOperations.POST.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Create user","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./user.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./user.operationMap.POST"},"vendorExtensions":{}},"/user/createWithArray":{"empty":false,"operationMap":{"POST":{"description":"","operationId":"createUsersWithArrayInput","parameters":[{"$ref":"$.changedEndpoints[8].changedOperations.POST.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Creates list of users with given input array","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./user/createWithArray.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./user/createWithArray.operationMap.POST"},"vendorExtensions":{}},"/user/createWithList":{"empty":false,"operationMap":{"POST":{"description":"","operationId":"createUsersWithListInput","parameters":[{"$ref":"$.changedEndpoints[9].changedOperations.POST.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Creates list of users with given input array","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.newSpecSwagger.paths./user/createWithList.operationMap.POST"}],"post":{"$ref":"$.newSpecSwagger.paths./user/createWithList.operationMap.POST"},"vendorExtensions":{}},"/user/login":{"empty":false,"get":{"description":"","operationId":"loginUser","parameters":[{"description":"The user name for login","in":"query","name":"username","required":true,"type":"string","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","format":"int32","required":false,"type":"integer","vendorExtensions":{}},"X-Expires-After":{"description":"date in UTC when token expires","format":"date-time","required":false,"type":"string","vendorExtensions":{}}},"schema":{"required":false,"type":"string","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid username/password supplied","vendorExtensions":{}}},"summary":"Logs user into the system","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./user/login.get"}},"operations":[{"$ref":"$.newSpecSwagger.paths./user/login.get"}],"vendorExtensions":{}},"/user/logout":{"empty":false,"get":{"description":"","operationId":"logoutUser","parameters":[],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Logs out current logged in user session","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./user/logout.get"}},"operations":[{"$ref":"$.newSpecSwagger.paths./user/logout.get"}],"vendorExtensions":{}},"/user/{username}":{"delete":{"description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"description":"The name that needs to be deleted","in":"path","name":"username","required":true,"type":"string","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid username supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Delete user","tags":["user"],"vendorExtensions":{}},"empty":false,"get":{"description":"","operationId":"getUserByName","parameters":[{"$ref":"$.changedEndpoints[11].changedOperations.GET.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid username supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Get user by user name","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.newSpecSwagger.paths./user/{username}.get"},"PUT":{"description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"description":"name that need to be updated","in":"path","name":"username","required":true,"type":"string","vendorExtensions":{}},{"$ref":"$.changedEndpoints[11].changedOperations.PUT.changedParameter[0].rightParameter"}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid user supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Updated user","tags":["user"],"vendorExtensions":{}},"DELETE":{"$ref":"$.newSpecSwagger.paths./user/{username}.delete"}},"operations":[{"$ref":"$.newSpecSwagger.paths./user/{username}.get"},{"$ref":"$.newSpecSwagger.paths./user/{username}.operationMap.PUT"},{"$ref":"$.newSpecSwagger.paths./user/{username}.delete"}],"put":{"$ref":"$.newSpecSwagger.paths./user/{username}.operationMap.PUT"},"vendorExtensions":{}}},"schemes":["HTTP"],"securityDefinitions":{"petstore_auth":{"authorizationUrl":"http://petstore.swagger.io/oauth/dialog","flow":"implicit","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"},"type":"oauth2","vendorExtensions":{}},"api_key":{"in":"HEADER","name":"api_key","type":"apiKey","vendorExtensions":{}}},"swagger":"2.0","tags":[{"description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io","vendorExtensions":{}},"name":"pet","vendorExtensions":{}},{"description":"Access to Petstore orders","name":"store","vendorExtensions":{}},{"description":"Operations about user","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io","vendorExtensions":{}},"name":"user","vendorExtensions":{}}]},"newVersion":"1.0.2","oldSpecSwagger":{"basePath":"/v2","definitions":{"Order":{"properties":{"id":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"petId":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"quantity":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"shipDate":{"format":"date-time","required":false,"type":"string","vendorExtensions":{}},"status":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"complete":{"default":false,"required":false,"type":"boolean","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Order"}},"Category":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"name":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[0].property"}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Category"}},"User":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"username":{"required":false,"type":"string","vendorExtensions":{}},"firstName":{"required":false,"type":"string","vendorExtensions":{}},"lastName":{"required":false,"type":"string","vendorExtensions":{}},"email":{"required":false,"type":"string","vendorExtensions":{}},"password":{"required":false,"type":"string","vendorExtensions":{}},"phone":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[1].property"},"favorite":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"userStatus":{"description":"User Status","format":"int32","required":false,"type":"integer","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"User"}},"Tag":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"name":{"required":false,"type":"string","vendorExtensions":{}},"removedField":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].missing[2].property"}},"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Tag"}},"Pet":{"properties":{"id":{"format":"int64","required":false,"type":"integer","vendorExtensions":{}},"category":{"refFormat":"INTERNAL","required":false,"simpleRef":"Category","type":"ref","vendorExtensions":{}},"name":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].changed[0].property"},"photoUrls":{"items":{"required":false,"type":"string","vendorExtensions":{}},"required":true,"type":"array","vendorExtensions":{},"xml":{"name":"photoUrl","wrapped":true}},"owner":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"parent":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"tags":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Tag","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{},"xml":{"name":"tag","wrapped":true}},"status":{"description":"pet status in the store","enum":["available","pending","sold"],"required":false,"type":"string","vendorExtensions":{}}},"required":["name","photoUrls"],"simple":false,"type":"object","vendorExtensions":{},"xml":{"name":"Pet"}},"ApiResponse":{"properties":{"code":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"type":{"required":false,"type":"string","vendorExtensions":{}},"message":{"required":false,"type":"string","vendorExtensions":{}}},"simple":false,"type":"object","vendorExtensions":{}}},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io","vendorExtensions":{}},"host":"petstore.swagger.io","info":{"contact":{"email":"apiteam@swagger.io"},"description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html","vendorExtensions":{}},"termsOfService":"http://swagger.io/terms/","title":"Swagger Petstore","vendorExtensions":{},"version":"1.0.0"},"paths":{"/pet":{"empty":false,"operationMap":{"PUT":{"consumes":["application/json","application/xml"],"description":"","operationId":"updatePet","parameters":[{"$ref":"$.changedEndpoints[0].changedOperations.PUT.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Pet not found","vendorExtensions":{}},"405":{"description":"Validation exception","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Update an existing pet","tags":["pet"],"vendorExtensions":{}},"POST":{"consumes":["application/json","application/xml"],"description":"","operationId":"addPet","parameters":[{"$ref":"$.changedEndpoints[0].changedOperations.POST.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"405":{"description":"Invalid input","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Add a new pet to the store","tags":["pet"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./pet.operationMap.PUT"},{"$ref":"$.oldSpecSwagger.paths./pet.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./pet.operationMap.POST"},"put":{"$ref":"$.oldSpecSwagger.paths./pet.operationMap.PUT"},"vendorExtensions":{}},"/pet/findByStatus":{"empty":false,"get":{"description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","parameters":[{"collectionFormat":"multi","description":"Status values that need to be considered for filter","in":"query","items":{"default":"available","enum":["available","pending","sold"],"required":false,"type":"string","vendorExtensions":{}},"name":"status","required":true,"type":"array","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid status value","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Finds Pets by status","tags":["pet"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./pet/findByStatus.get"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./pet/findByStatus.get"}],"vendorExtensions":{}},"/pet/findByTags":{"empty":false,"get":{"deprecated":true,"description":"Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"collectionFormat":"multi","description":"Tags to filter by","in":"query","items":{"required":false,"type":"string","vendorExtensions":{}},"name":"tags","required":true,"type":"array","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"items":{"refFormat":"INTERNAL","required":false,"simpleRef":"Pet","type":"ref","vendorExtensions":{}},"required":false,"type":"array","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid tag value","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Finds Pets by tags","tags":["pet"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./pet/findByTags.get"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./pet/findByTags.get"}],"vendorExtensions":{}},"/pet/{petId}":{"delete":{"description":"","operationId":"deletePet","parameters":[{"in":"header","name":"api_key","required":false,"type":"string","vendorExtensions":{}},{"description":"Pet id to delete","format":"int64","in":"path","name":"petId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Pet not found","vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Deletes a pet","tags":["pet"],"vendorExtensions":{}},"empty":false,"operationMap":{"POST":{"$ref":"$.changedEndpoints[3].missingOperations.POST"},"DELETE":{"$ref":"$.oldSpecSwagger.paths./pet/{petId}.delete"}},"operations":[{"$ref":"$.changedEndpoints[3].missingOperations.POST"},{"$ref":"$.oldSpecSwagger.paths./pet/{petId}.delete"}],"post":{"$ref":"$.changedEndpoints[3].missingOperations.POST"},"vendorExtensions":{}},"/pet/{petId}/uploadImage":{"empty":false,"operationMap":{"POST":{"consumes":["multipart/form-data"],"description":"","operationId":"uploadFile","parameters":[{"$ref":"$.changedEndpoints[4].changedOperations.POST.changedParameter[0].leftParameter"},{"description":"Additional data to pass to server","in":"formData","name":"additionalMetadata","required":false,"type":"string","vendorExtensions":{}},{"description":"file to upload","in":"formData","name":"file","required":false,"type":"file","vendorExtensions":{}}],"produces":["application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"ApiResponse","type":"ref","vendorExtensions":{}},"vendorExtensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"uploads an image","tags":["pet"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./pet/{petId}/uploadImage.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./pet/{petId}/uploadImage.operationMap.POST"},"vendorExtensions":{}},"/store/inventory":{"empty":false,"get":{"description":"Returns a map of status codes to quantities","operationId":"getInventory","parameters":[],"produces":["application/json"],"responses":{"200":{"description":"successful operation","schema":{"additionalProperties":{"format":"int32","required":false,"type":"integer","vendorExtensions":{}},"required":false,"type":"object","vendorExtensions":{}},"vendorExtensions":{}}},"security":[{"api_key":[]}],"summary":"Returns pet inventories by status","tags":["store"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./store/inventory.get"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./store/inventory.get"}],"vendorExtensions":{}},"/store/order":{"empty":false,"operationMap":{"POST":{"consumes":["application/xml"],"description":"","operationId":"placeOrder","parameters":[{"description":"order placed for purchasing the pet","in":"body","name":"body","required":true,"schema":{"refFormat":"INTERNAL","reference":"#/definitions/Order","simpleRef":"Order"},"vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"Order","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid Order","vendorExtensions":{}}},"summary":"Place an order for a pet","tags":["store"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./store/order.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./store/order.operationMap.POST"},"vendorExtensions":{}},"/store/order/{orderId}":{"delete":{"description":"For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors","operationId":"deleteOrder","parameters":[{"description":"ID of the order that needs to be deleted","format":"int64","in":"path","minimum":1,"name":"orderId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Order not found","vendorExtensions":{}}},"summary":"Delete purchase order by ID","tags":["store"],"vendorExtensions":{}},"empty":false,"get":{"description":"For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions","operationId":"getOrderById","parameters":[{"description":"ID of pet that needs to be fetched","format":"int64","in":"path","maximum":10,"minimum":1,"name":"orderId","required":true,"type":"integer","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"Order","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid ID supplied","vendorExtensions":{}},"404":{"description":"Order not found","vendorExtensions":{}}},"summary":"Find purchase order by ID","tags":["store"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./store/order/{orderId}.get"},"DELETE":{"$ref":"$.oldSpecSwagger.paths./store/order/{orderId}.delete"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./store/order/{orderId}.get"},{"$ref":"$.oldSpecSwagger.paths./store/order/{orderId}.delete"}],"vendorExtensions":{}},"/user":{"empty":false,"operationMap":{"POST":{"description":"This can only be done by the logged in user.","operationId":"createUser","parameters":[{"$ref":"$.changedEndpoints[7].changedOperations.POST.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Create user","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./user.operationMap.POST"},"vendorExtensions":{}},"/user/createWithArray":{"empty":false,"operationMap":{"POST":{"description":"","operationId":"createUsersWithArrayInput","parameters":[{"$ref":"$.changedEndpoints[8].changedOperations.POST.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Creates list of users with given input array","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user/createWithArray.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./user/createWithArray.operationMap.POST"},"vendorExtensions":{}},"/user/createWithList":{"empty":false,"operationMap":{"POST":{"description":"","operationId":"createUsersWithListInput","parameters":[{"$ref":"$.changedEndpoints[9].changedOperations.POST.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Creates list of users with given input array","tags":["user"],"vendorExtensions":{}}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user/createWithList.operationMap.POST"}],"post":{"$ref":"$.oldSpecSwagger.paths./user/createWithList.operationMap.POST"},"vendorExtensions":{}},"/user/login":{"empty":false,"get":{"description":"","operationId":"loginUser","parameters":[{"description":"The user name for login","in":"query","name":"username","required":true,"type":"string","vendorExtensions":{}},{"$ref":"$.changedEndpoints[10].changedOperations.GET.missingParameters[0]"}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","format":"int32","required":false,"type":"integer","vendorExtensions":{}},"X-Expires-After":{"description":"date in UTC when token expires","format":"date-time","required":false,"type":"string","vendorExtensions":{}}},"schema":{"required":false,"type":"string","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid username/password supplied","vendorExtensions":{}}},"summary":"Logs user into the system","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./user/login.get"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user/login.get"}],"vendorExtensions":{}},"/user/logout":{"empty":false,"get":{"description":"","operationId":"logoutUser","parameters":[],"produces":["application/xml","application/json"],"responses":{"default":{"description":"successful operation","vendorExtensions":{}}},"summary":"Logs out current logged in user session","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./user/logout.get"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user/logout.get"}],"vendorExtensions":{}},"/user/{username}":{"delete":{"description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"description":"The name that needs to be deleted","in":"path","name":"username","required":true,"type":"string","vendorExtensions":{}}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid username supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Delete user","tags":["user"],"vendorExtensions":{}},"empty":false,"get":{"description":"","operationId":"getUserByName","parameters":[{"$ref":"$.changedEndpoints[11].changedOperations.GET.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"200":{"description":"successful operation","schema":{"refFormat":"INTERNAL","required":false,"simpleRef":"User","type":"ref","vendorExtensions":{}},"vendorExtensions":{}},"400":{"description":"Invalid username supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Get user by user name","tags":["user"],"vendorExtensions":{}},"operationMap":{"GET":{"$ref":"$.oldSpecSwagger.paths./user/{username}.get"},"PUT":{"description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"description":"name that need to be updated","in":"path","name":"username","required":true,"type":"string","vendorExtensions":{}},{"$ref":"$.changedEndpoints[11].changedOperations.PUT.changedParameter[0].leftParameter"}],"produces":["application/xml","application/json"],"responses":{"400":{"description":"Invalid user supplied","vendorExtensions":{}},"404":{"description":"User not found","vendorExtensions":{}}},"summary":"Updated user","tags":["user"],"vendorExtensions":{}},"DELETE":{"$ref":"$.oldSpecSwagger.paths./user/{username}.delete"}},"operations":[{"$ref":"$.oldSpecSwagger.paths./user/{username}.get"},{"$ref":"$.oldSpecSwagger.paths./user/{username}.operationMap.PUT"},{"$ref":"$.oldSpecSwagger.paths./user/{username}.delete"}],"put":{"$ref":"$.oldSpecSwagger.paths./user/{username}.operationMap.PUT"},"vendorExtensions":{}}},"schemes":["HTTP"],"securityDefinitions":{"petstore_auth":{"authorizationUrl":"http://petstore.swagger.io/oauth/dialog","flow":"implicit","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"},"type":"oauth2","vendorExtensions":{}},"api_key":{"in":"HEADER","name":"api_key","type":"apiKey","vendorExtensions":{}}},"swagger":"2.0","tags":[{"description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io","vendorExtensions":{}},"name":"pet","vendorExtensions":{}},{"description":"Access to Petstore orders","name":"store","vendorExtensions":{}},{"description":"Operations about user","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io","vendorExtensions":{}},"name":"user","vendorExtensions":{}}]},"oldVersion":"1.0.0"} \ No newline at end of file diff --git a/testDiff.md b/testDiff.md deleted file mode 100644 index 3764b276..00000000 --- a/testDiff.md +++ /dev/null @@ -1,71 +0,0 @@ -## Version 1.0.0 to 1.0.2 ---- -### What's New ---- -* `GET` /pet/{petId} Find pet by ID - -### What's Deprecated ---- -* `POST` /pet/{petId} Updates a pet in the store with form data - -### What's Changed ---- -`POST` /pet Add a new pet to the store - Parameters - - Add tags //add new query param demo - Insert body.newFeild //a feild demo by sayi - Insert body.category.newCatFeild - Insert body.owner.newUserFeild //a new user feild demo - Delete body.category.name - Delete body.owner.phone - Modify body.name -`PUT` /pet Update an existing pet - Parameters - - Insert body.newFeild //a feild demo by sayi - Insert body.category.newCatFeild - Insert body.owner.newUserFeild //a new user feild demo - Delete body.category.name - Delete body.owner.phone - Modify body.name -`DELETE` /pet/{petId} Deletes a pet - Parameters - - Add newHeaderParam -`POST` /pet/{petId}/uploadImage uploads an image for pet - Parameters - - Add petId //ID of pet to update, default false - petId change into not required Notes ID of pet to update change into ID of pet to update, default false -`POST` /user Create user - Parameters - - Insert body.newUserFeild //a new user feild demo - Insert body.favorite.newFeild //a feild demo by sayi - Insert body.favorite.category.newCatFeild - Delete body.phone - Delete body.favorite.category.name - Modify body.favorite.name -`GET` /user/login Logs user into the system - Parameters - - Delete password //The password for login in clear text -`PUT` /user/{username} Updated user - Parameters - - Insert body.newUserFeild //a new user feild demo - Insert body.favorite.newFeild //a feild demo by sayi - Insert body.favorite.category.newCatFeild - Delete body.phone - Delete body.favorite.category.name - Modify body.favorite.name -`GET` /user/{username} Get user by user name - Return Type - - Insert newUserFeild //a new user feild demo - Insert favorite.newFeild //a feild demo by sayi - Insert favorite.category.newCatFeild - Delete phone - Delete favorite.category.name - Modify favorite.name From eca8ae54b17ac527c4020dec554a838f648afb5b Mon Sep 17 00:00:00 2001 From: jlamaille Date: Thu, 2 Jul 2020 14:57:57 +0200 Subject: [PATCH 17/18] Upgrade lombok --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1d3588ff..65f23e66 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.projectlombok lombok - 1.16.20 + 1.18.12 From 57421be6479fcf206a2ea0f932235faf4a3b1cbc Mon Sep 17 00:00:00 2001 From: jlamaille Date: Fri, 3 Jul 2020 09:54:10 +0200 Subject: [PATCH 18/18] Rollback upgrade lombok --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65f23e66..1d3588ff 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ org.projectlombok lombok - 1.18.12 + 1.16.20