Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adds support for writeOnly #823

Merged
merged 1 commit into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
]
}
]
}
]