getExpectations();
+
+ protected abstract String getMessageTemplate();
+
+ protected boolean skipMissingNode() {
+ return false;
+ }
+}
diff --git a/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueEquals.java b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueEquals.java
index 082d6b7..939aad3 100644
--- a/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueEquals.java
+++ b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueEquals.java
@@ -1,54 +1,30 @@
package com.optum.sourcehawk.enforcer.file.json;
-import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
import com.optum.sourcehawk.core.utils.StringUtils;
-import com.optum.sourcehawk.enforcer.EnforcerResult;
-import com.optum.sourcehawk.enforcer.ResolverResult;
-import com.optum.sourcehawk.enforcer.file.AbstractFileEnforcer;
import com.optum.sourcehawk.enforcer.file.FileResolver;
import lombok.AllArgsConstructor;
import lombok.Builder;
-import lombok.NonNull;
-import lombok.val;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.math.BigDecimal;
-import java.math.BigInteger;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
/**
* An enforcer implementation which enforces that the result of a JsonPath query equals a specific value
*
* @author Brian Wyka
+ * @author Christian Oestreich
*/
@Builder(builderClassName = "Builder")
@JsonDeserialize(builder = JsonValueEquals.Builder.class)
@AllArgsConstructor(staticName = "equals")
-public class JsonValueEquals extends AbstractFileEnforcer implements FileResolver {
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- private static final String READ_ERROR_TEMPLATE = "Reading or parsing file resulted in error [%s]";
- private static final String QUERY_ERROR_TEMPLATE = "Execution of pointer expression [%s] yielded error [%s]";
- private static final String MISSING_MESSAGE_TEMPLATE = "Execution of pointer expression [%s] yielded no result";
- private static final String NOT_EQUAL_MESSAGE_TEMPLATE = "Execution of pointer expression [%s] yielded result [%s] which is not equal to [%s]";
- private static final String UPDATE_MESSAGE_TEMPLATE = "Pointer expression [%s] has been updated with value [%s]";
- private static final String UPDATE_MISSING_MESSAGE_TEMPLATE = "Pointer expression [%s] which was missing, has been set with value [%s]";
+public class JsonValueEquals extends AbstractJsonValue implements FileResolver {
/**
* Key: The JsonPointer expression to retrieve the value
- *
+ *
* Value: The expected value which the query should evaluate to
*/
private final Map expectations;
@@ -57,199 +33,19 @@ public class JsonValueEquals extends AbstractFileEnforcer implements FileResolve
* Create with a single path query and expected value
*
* @param jsonPointerExpression the JSON pointer expression
- * @param expectedValue the expected value
+ * @param expectedValue the expected value
* @return the enforcer
*/
public static JsonValueEquals equals(final String jsonPointerExpression, final Object expectedValue) {
return JsonValueEquals.equals(Collections.singletonMap(jsonPointerExpression, expectedValue));
}
- /** {@inheritDoc} */
@Override
- public EnforcerResult enforceInternal(@NonNull final InputStream actualFileInputStream) {
- final JsonNode jsonNode;
- try {
- jsonNode = OBJECT_MAPPER.readTree(actualFileInputStream);
- } catch (final IOException e) {
- return EnforcerResult.failed(String.format(READ_ERROR_TEMPLATE, e.getMessage()));
- }
- val messages = expectations.entrySet()
- .stream()
- .map(entry -> enforce(jsonNode, entry.getKey(), entry.getValue()))
- .filter(Optional::isPresent)
- .map(Optional::get)
- .collect(Collectors.toSet());
- return EnforcerResult.create(messages);
- }
-
- /**
- * Enforce individual json path queries with expected value
- *
- * @param jsonNode the JSON node
- * @param jsonPointerExpression the JSON pointer expression
- * @param expectedValue the expected value
- * @return The message to be added, otherwise {@link Optional#empty()}
- */
- private static Optional enforce(final JsonNode jsonNode, final String jsonPointerExpression, final Object expectedValue) {
- try {
- val actualJsonNode = jsonNode.at(JsonPointer.compile(jsonPointerExpression));
- if (actualJsonNode == null || actualJsonNode.isMissingNode()) {
- return Optional.of(String.format(MISSING_MESSAGE_TEMPLATE, jsonPointerExpression));
- }
- if (jsonNodeValueEquals(actualJsonNode, expectedValue)) {
- return Optional.empty();
- }
- return Optional.of(String.format(NOT_EQUAL_MESSAGE_TEMPLATE, jsonPointerExpression, actualJsonNode.asText(), expectedValue));
- } catch (final Exception e) {
- return Optional.of(String.format(QUERY_ERROR_TEMPLATE, jsonPointerExpression, e.getMessage()));
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public ResolverResult resolve(final @NonNull InputStream actualFileInputStream, final @NonNull Writer outputFileWriter) throws IOException {
- final JsonNode rootJsonNode;
- try {
- rootJsonNode = OBJECT_MAPPER.readTree(actualFileInputStream);
- } catch (final IOException e) {
- return ResolverResult.error(String.format(READ_ERROR_TEMPLATE, e.getMessage()));
- }
- val resolverResult = expectations.entrySet().stream()
- .map(entry -> resolve(rootJsonNode, entry.getKey(), entry.getValue()))
- .reduce(ResolverResult.builder().error(true).build(), ResolverResult::reduce);
- if (resolverResult.isUpdatesApplied()) {
- outputFileWriter.write(rootJsonNode.toPrettyString());
- }
- return resolverResult;
- }
-
- /**
- * Resolve an individual json path query with the expected value
- *
- * @param rootJsonNode the root JSON node
- * @param jsonPointerExpression the JSON pointer expression
- * @param expectedValue the expected value
- * @return the resolver result
- */
- private static ResolverResult resolve(final JsonNode rootJsonNode, final String jsonPointerExpression, final Object expectedValue) {
- try {
- val jsonPointer = JsonPointer.compile(jsonPointerExpression);
- val actualJsonNode = rootJsonNode.at(jsonPointer);
- final String resolverResultMessage;
- if (actualJsonNode.isMissingNode()) {
- resolverResultMessage = String.format(UPDATE_MISSING_MESSAGE_TEMPLATE, jsonPointerExpression, expectedValue);
- } else {
- resolverResultMessage = String.format(UPDATE_MESSAGE_TEMPLATE, jsonPointerExpression, expectedValue);
- }
- if (jsonNodeValueEquals(actualJsonNode, expectedValue)) {
- return ResolverResult.NO_UPDATES;
- }
- val parentNode = rootJsonNode.at(jsonPointer.head());
- if (parentNode instanceof ObjectNode) {
- updateObjectNodeValue((ObjectNode) parentNode, jsonPointer.last().toString().substring(1), expectedValue);
- } else if (parentNode instanceof ArrayNode) {
- updateArrayNodeValue((ArrayNode) parentNode, Integer.parseInt(jsonPointer.last().toString().substring(1)), expectedValue, actualJsonNode.isMissingNode());
- } else {
- return ResolverResult.error("Update not supported for given pointer expression");
- }
- return ResolverResult.updatesApplied(resolverResultMessage);
- } catch (final Exception e) {
- return ResolverResult.error(String.format(QUERY_ERROR_TEMPLATE, jsonPointerExpression, e.getMessage()));
- }
+ protected Map getExpectations() {
+ return this.expectations;
}
- /**
- * Update the actual node with the expected value
- *
- * @param parentObjectNode the parent object node
- * @param childNodeName the child node name
- * @param expectedValue the expected value
- */
- private static void updateObjectNodeValue(final ObjectNode parentObjectNode, final String childNodeName, final Object expectedValue) {
- if (expectedValue instanceof Boolean) {
- parentObjectNode.put(childNodeName, (boolean) expectedValue);
- } else if (expectedValue instanceof Integer) {
- parentObjectNode.put(childNodeName, (int) expectedValue);
- } else if (expectedValue instanceof Long) {
- parentObjectNode.put(childNodeName, (long) expectedValue);
- } else if (expectedValue instanceof Short) {
- parentObjectNode.put(childNodeName, (short) expectedValue);
- } else if (expectedValue instanceof BigDecimal) {
- parentObjectNode.put(childNodeName, (BigDecimal) expectedValue);
- } else if (expectedValue instanceof BigInteger) {
- parentObjectNode.put(childNodeName, (BigInteger) expectedValue);
- } else {
- parentObjectNode.put(childNodeName, expectedValue.toString());
- }
+ protected String getMessageTemplate(){
+ return "Execution of pointer expression [%s] yielded result [%s] which is not equal to [%s]";
}
-
- /**
- * Update the actual node with the expected value
- *
- * @param parentArrayNode the parent object node
- * @param actualNodeIndex the actual node index in the array
- * @param expectedValue the expected value
- * @param missing true if node does not currently exist, false otherwise
- */
- @SuppressWarnings("squid:S3776")
- private static void updateArrayNodeValue(final ArrayNode parentArrayNode, final int actualNodeIndex, final Object expectedValue, final boolean missing) {
- if (expectedValue instanceof Boolean) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (boolean) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.booleanNode((boolean) expectedValue));
- }
- } else if (expectedValue instanceof Integer) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (int) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.numberNode((int) expectedValue));
- }
- } else if (expectedValue instanceof Long) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (long) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.numberNode((long) expectedValue));
- }
- } else if (expectedValue instanceof Short) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (short) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.numberNode((short) expectedValue));
- }
- } else if (expectedValue instanceof BigDecimal) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (BigDecimal) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.numberNode((BigDecimal) expectedValue));
- }
- } else if (expectedValue instanceof BigInteger) {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, (BigInteger) expectedValue);
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.numberNode((BigInteger) expectedValue));
- }
- } else {
- if (missing) {
- parentArrayNode.insert(actualNodeIndex, expectedValue.toString());
- } else {
- parentArrayNode.set(actualNodeIndex, JsonNodeFactory.instance.textNode(expectedValue.toString()));
- }
- }
- }
-
- /**
- * Determine if the {@code jsonNode} value equals that of the {@code expectedValue}
- *
- * @param jsonNode the JSON node to retrieve the value from
- * @param expectedValue the expected value
- * @return true if they are equal, false otherwise
- */
- private static boolean jsonNodeValueEquals(final JsonNode jsonNode, final Object expectedValue) {
- return (expectedValue instanceof CharSequence && StringUtils.equals((CharSequence) expectedValue, jsonNode.textValue()))
- || (expectedValue instanceof Number && expectedValue == jsonNode.numberValue())
- || (expectedValue instanceof Boolean && (boolean) expectedValue == jsonNode.booleanValue())
- || Objects.equals(expectedValue.toString(), jsonNode.asText());
- }
-
}
diff --git a/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueNotEquals.java b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueNotEquals.java
new file mode 100644
index 0000000..b0982cb
--- /dev/null
+++ b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/json/JsonValueNotEquals.java
@@ -0,0 +1,76 @@
+package com.optum.sourcehawk.enforcer.file.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.optum.sourcehawk.core.utils.StringUtils;
+import com.optum.sourcehawk.enforcer.file.FileResolver;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * An enforcer implementation which enforces that the result of a JsonPath query does not equal a specific value
+ *
+ * @author Brian Wyka
+ * @author Christian Oestreich
+ */
+@Builder(builderClassName = "Builder")
+@JsonDeserialize(builder = JsonValueNotEquals.Builder.class)
+@AllArgsConstructor(staticName = "equals")
+public class JsonValueNotEquals extends AbstractJsonValue implements FileResolver {
+
+ /**
+ * Key: The JsonPointer expression to retrieve the value
+ *
+ * Value: The expected value which the query should evaluate to
+ */
+ private final Map expectations;
+
+ /**
+ * Create with a single path query and expected value
+ *
+ * @param jsonPointerExpression the JSON pointer expression
+ * @param expectedValue the expected value
+ * @return the enforcer
+ */
+ public static JsonValueNotEquals equals(final String jsonPointerExpression, final Object expectedValue) {
+ return JsonValueNotEquals.equals(Collections.singletonMap(jsonPointerExpression, expectedValue));
+ }
+
+ /**
+ * Determine if the {@code jsonNode} value equals that of the {@code expectedValue}
+ *
+ * @param jsonNode the JSON node to retrieve the value from
+ * @param expectedValue the expected value
+ * @return true if they are equal, false otherwise
+ */
+ protected boolean jsonNodeValueEquals(final JsonNode jsonNode, final Object expectedValue) {
+ if (expectedValue instanceof CharSequence) {
+ return !StringUtils.equals((CharSequence) expectedValue, jsonNode.textValue());
+ }
+ if (expectedValue instanceof Number) {
+ return expectedValue != jsonNode.numberValue();
+ }
+ if (expectedValue instanceof Boolean) {
+ return (boolean) expectedValue != jsonNode.booleanValue();
+ }
+ return !Objects.equals(expectedValue.toString(), jsonNode.asText());
+ }
+
+ @Override
+ protected Map getExpectations() {
+ return this.expectations;
+ }
+
+ @Override
+ protected String getMessageTemplate() {
+ return "Execution of pointer expression [%s] yielded result [%s] which does equal [%s]";
+ }
+
+ protected boolean skipMissingNode(){
+ return true;
+ }
+}
diff --git a/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/yaml/YamlValueNotEquals.java b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/yaml/YamlValueNotEquals.java
new file mode 100644
index 0000000..069c0c4
--- /dev/null
+++ b/enforcer/file/common/src/main/java/com/optum/sourcehawk/enforcer/file/yaml/YamlValueNotEquals.java
@@ -0,0 +1,69 @@
+package com.optum.sourcehawk.enforcer.file.yaml;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.optum.sourcehawk.enforcer.EnforcerResult;
+import com.optum.sourcehawk.enforcer.file.AbstractFileEnforcer;
+import com.optum.sourcehawk.enforcer.file.json.JsonValueEquals;
+import com.optum.sourcehawk.enforcer.file.json.JsonValueNotEquals;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.NonNull;
+import lombok.val;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An enforcer which is responsible for enforcing that a yaml file has a specific property with an expected value. Under
+ * the hood, this is delegating to {@link JsonValueEquals} after converting the yaml to json
+ *
+ * @author Christian Oestreich
+ * @see JsonValueNotEquals
+ */
+@Builder(builderClassName = "Builder")
+@JsonDeserialize(builder = YamlValueNotEquals.Builder.class)
+@AllArgsConstructor(staticName = "equals")
+public class YamlValueNotEquals extends AbstractFileEnforcer {
+
+ private static final ObjectMapper YAML_MAPPER = new YAMLMapper();
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ /**
+ * Key: The Yaml Pointer expression to retrieve the value
+ *
+ * Value: The expected value which the query should evaluate to
+ */
+ private final Map expectations;
+
+ /**
+ * Create with a single path query and expected value
+ *
+ * @param yamlPathQuery the yaml path query
+ * @param expectedValue the expected value
+ * @return the enforcer
+ */
+ public static YamlValueNotEquals equals(final String yamlPathQuery, final Object expectedValue) {
+ return YamlValueNotEquals.equals(Collections.singletonMap(yamlPathQuery, expectedValue));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public EnforcerResult enforceInternal(@NonNull final InputStream actualFileInputStream) throws IOException {
+ val yamlMap = YAML_MAPPER.readValue(actualFileInputStream, new TypeReference