Skip to content

Commit

Permalink
Adds support for writeOnly (#823)
Browse files Browse the repository at this point in the history
Resolves #798

Co-authored-by: Faron Dutton <faron.dutton@insightglobal.com>
  • Loading branch information
fdutton and Faron Dutton authored Jun 14, 2023
1 parent 200d9e9 commit d353623
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 18 deletions.
20 changes: 10 additions & 10 deletions src/main/java/com/networknt/schema/ReadOnlyValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,24 @@

import com.fasterxml.jackson.databind.JsonNode;

public class ReadOnlyValidator extends BaseJsonValidator implements JsonValidator {
private static final Logger logger = LoggerFactory.getLogger(RequiredValidator.class);
public class ReadOnlyValidator extends BaseJsonValidator {
private static final Logger logger = LoggerFactory.getLogger(ReadOnlyValidator.class);

private Boolean writeMode;
private final boolean readOnly;

public ReadOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext) {
public ReadOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.READ_ONLY, validationContext);
this.writeMode = validationContext.getConfig().isWriteMode();
String mode = writeMode ? "write mode" : "read mode";
logger.debug("Loaded ReadOnlyValidator for property {} as {}", parentSchema, mode);

this.readOnly = validationContext.getConfig().isReadOnly();
logger.debug("Loaded ReadOnlyValidator for property {} as {}", parentSchema, "read mode");
parseErrorCode(getValidatorType().getErrorCodeKey());
}

@Override
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);
Set<ValidationMessage> errors= new HashSet<ValidationMessage>();
if (writeMode) {
Set<ValidationMessage> errors= new HashSet<>();
if (this.readOnly) {
errors.add(buildValidationMessage(at));
}
return errors;
Expand Down
39 changes: 35 additions & 4 deletions src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,14 @@ public class SchemaValidatorsConfig {
private boolean resetCollectorContext = true;

/**
* When set to true considers that schema is used to write data then ReadOnlyValidator is activated. Default true.
* When set to true assumes that schema is used to validate incoming data from an API.
*/
private boolean writeMode = true;
private Boolean readOnly = null;

/**
* When set to true assumes that schema is used to to validate outgoing data from an API.
*/
private Boolean writeOnly = null;

/**
* The approach used to generate paths in reported messages, logs and errors. Default is the legacy "JSONPath-like" approach.
Expand Down Expand Up @@ -437,18 +442,44 @@ public void setResetCollectorContext(boolean resetCollectorContext) {
this.resetCollectorContext = resetCollectorContext;
}

public boolean isReadOnly() {
return null != this.readOnly && this.readOnly;
}

public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}

public boolean isWriteOnly() {
return null != this.writeOnly && this.writeOnly;
}

public void setWriteOnly(boolean writeOnly) {
this.writeOnly = writeOnly;
}

/**
* Use {@code isReadOnly} or {@code isWriteOnly}
*/
@Deprecated
public boolean isWriteMode() {
return this.writeMode;
return null == this.writeOnly || this.writeOnly;
}

/**
*
* When set to true considers that schema is used to write data then ReadOnlyValidator is activated. Default true.
*
* @param writeMode true if schema is used to write data
* @deprecated Use {@code setReadOnly} or {@code setWriteOnly}
*/
@Deprecated
public void setWriteMode(boolean writeMode) {
this.writeMode = writeMode;
if (writeMode) {
setWriteOnly(true);
} else {
setReadOnly(true);
}
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/networknt/schema/ValidatorTypeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
PREFIX_ITEMS("prefixItems", "1048", PrefixItemsValidator.class, VersionCode.MinV202012),
PROPERTIES("properties", "1025", PropertiesValidator.class, VersionCode.AllVersions),
PROPERTYNAMES("propertyNames", "1044", PropertyNamesValidator.class, VersionCode.MinV6),
READ_ONLY("readOnly", "1032", ReadOnlyValidator.class, VersionCode.AllVersions),
READ_ONLY("readOnly", "1032", ReadOnlyValidator.class, VersionCode.MinV7),
REF("$ref", "1026", RefValidator.class, VersionCode.AllVersions),
REQUIRED("required", "1028", RequiredValidator.class, VersionCode.AllVersions),
TRUE("true", "1040", TrueValidator.class, VersionCode.MinV6),
Expand All @@ -103,6 +103,7 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
UNION_TYPE("unionType", "1030", UnionTypeValidator.class, VersionCode.AllVersions),
UNIQUE_ITEMS("uniqueItems", "1031", UniqueItemsValidator.class, VersionCode.AllVersions),
UUID("uuid", "1035", null, VersionCode.AllVersions),
WRITE_ONLY("writeOnly", "1027", WriteOnlyValidator.class, VersionCode.MinV7),
;

private static final Map<String, ValidatorTypeCode> CONSTANTS = new HashMap<>();
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/networknt/schema/WriteOnlyValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.networknt.schema;

import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;

public class WriteOnlyValidator extends BaseJsonValidator {
private static final Logger logger = LoggerFactory.getLogger(WriteOnlyValidator.class);

private final boolean writeOnly;

public WriteOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.WRITE_ONLY, validationContext);

this.writeOnly = validationContext.getConfig().isWriteOnly();
logger.debug("Loaded WriteOnlyValidator for property {} as {}", parentSchema, "write mode");
parseErrorCode(getValidatorType().getErrorCodeKey());
}

@Override
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);
Set<ValidationMessage> errors= new HashSet<>();
if (this.writeOnly) {
errors.add(buildValidationMessage(at));
}
return errors;
}

}
1 change: 1 addition & 0 deletions src/main/resources/jsv-messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ unevaluatedProperties = There are unevaluated properties at the following paths
unionType = {0}: {1} found, but {2} is required
uniqueItems = {0}: the items in the array must be unique
uuid = {0}: {1} is an invalid {2}
writeOnly = {0}: is a write-only field, it cannot appear in the data
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,17 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS
config.setTypeLoose(typeLoose);
config.setEcma262Validator(TestSpec.RegexKind.JDK != testSpec.getRegex());
testSpec.getStrictness().forEach(config::setStrict);
if (testSpec.getConfig() != null && testSpec.getConfig().containsKey("isCustomMessageSupported")) {
config.setCustomMessageSupported((Boolean) testSpec.getConfig().get("isCustomMessageSupported"));

if (testSpec.getConfig() != null) {
if (testSpec.getConfig().containsKey("isCustomMessageSupported")) {
config.setCustomMessageSupported((Boolean) testSpec.getConfig().get("isCustomMessageSupported"));
}
if (testSpec.getConfig().containsKey("readOnly")) {
config.setReadOnly((Boolean) testSpec.getConfig().get("readOnly"));
}
if (testSpec.getConfig().containsKey("writeOnly")) {
config.setWriteOnly((Boolean) testSpec.getConfig().get("writeOnly"));
}
}

URI testCaseFileUri = URI.create("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private JsonSchema getJsonSchema(Boolean write) {

private SchemaValidatorsConfig createSchemaConfig(Boolean write) {
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
config.setWriteMode(write);
config.setReadOnly(write);
return config;
}

Expand Down
46 changes: 46 additions & 0 deletions src/test/resources/draft2020-12/issue798.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[
{
"description": "issue798",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"a": { "type": "string" },
"b": { "type": "string", "readOnly": true },
"c": { "type": "string", "writeOnly": true },
"d": { "type": "string", "readOnly": true, "writeOnly": true }
}
},
"tests": [
{
"description": "default behavior",
"data": { "a": "a string" },
"valid": true,
"config": { "readOnly": true, "writeOnly": true }
},
{
"description": "readonly behavior",
"data": { "a": "a string", "b": "a string" },
"valid": false,
"config": { "readOnly": true, "writeOnly": true },
"validationMessages": [ "$.b: is a readonly field, it cannot be changed" ]
},
{
"description": "write-only behavior",
"data": { "a": "a string", "c": "a string" },
"valid": false,
"config": { "readOnly": true, "writeOnly": true },
"validationMessages": [ "$.c: is a write-only field, it cannot appear in the data" ]
},
{
"description": "both behavior",
"data": { "a": "a string", "d": "a string" },
"valid": false,
"config": { "readOnly": true, "writeOnly": true },
"validationMessages": [
"$.d: is a readonly field, it cannot be changed",
"$.d: is a write-only field, it cannot appear in the data"
]
}
]
}
]

0 comments on commit d353623

Please sign in to comment.