Skip to content

Commit d353623

Browse files
fduttonFaron Dutton
andauthored
Adds support for writeOnly (#823)
Resolves #798 Co-authored-by: Faron Dutton <[email protected]>
1 parent 200d9e9 commit d353623

File tree

8 files changed

+140
-18
lines changed

8 files changed

+140
-18
lines changed

src/main/java/com/networknt/schema/ReadOnlyValidator.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@
2424

2525
import com.fasterxml.jackson.databind.JsonNode;
2626

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

30-
private Boolean writeMode;
30+
private final boolean readOnly;
3131

32-
public ReadOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
33-
ValidationContext validationContext) {
32+
public ReadOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3433
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.READ_ONLY, validationContext);
35-
this.writeMode = validationContext.getConfig().isWriteMode();
36-
String mode = writeMode ? "write mode" : "read mode";
37-
logger.debug("Loaded ReadOnlyValidator for property {} as {}", parentSchema, mode);
34+
35+
this.readOnly = validationContext.getConfig().isReadOnly();
36+
logger.debug("Loaded ReadOnlyValidator for property {} as {}", parentSchema, "read mode");
3837
parseErrorCode(getValidatorType().getErrorCodeKey());
3938
}
4039

40+
@Override
4141
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
4242
debug(logger, node, rootNode, at);
43-
Set<ValidationMessage> errors= new HashSet<ValidationMessage>();
44-
if (writeMode) {
43+
Set<ValidationMessage> errors= new HashSet<>();
44+
if (this.readOnly) {
4545
errors.add(buildValidationMessage(at));
4646
}
4747
return errors;

src/main/java/com/networknt/schema/SchemaValidatorsConfig.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,14 @@ public class SchemaValidatorsConfig {
110110
private boolean resetCollectorContext = true;
111111

112112
/**
113-
* When set to true considers that schema is used to write data then ReadOnlyValidator is activated. Default true.
113+
* When set to true assumes that schema is used to validate incoming data from an API.
114114
*/
115-
private boolean writeMode = true;
115+
private Boolean readOnly = null;
116+
117+
/**
118+
* When set to true assumes that schema is used to to validate outgoing data from an API.
119+
*/
120+
private Boolean writeOnly = null;
116121

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

445+
public boolean isReadOnly() {
446+
return null != this.readOnly && this.readOnly;
447+
}
448+
449+
public void setReadOnly(boolean readOnly) {
450+
this.readOnly = readOnly;
451+
}
452+
453+
public boolean isWriteOnly() {
454+
return null != this.writeOnly && this.writeOnly;
455+
}
456+
457+
public void setWriteOnly(boolean writeOnly) {
458+
this.writeOnly = writeOnly;
459+
}
460+
461+
/**
462+
* Use {@code isReadOnly} or {@code isWriteOnly}
463+
*/
464+
@Deprecated
440465
public boolean isWriteMode() {
441-
return this.writeMode;
466+
return null == this.writeOnly || this.writeOnly;
442467
}
443468

444469
/**
445470
*
446471
* When set to true considers that schema is used to write data then ReadOnlyValidator is activated. Default true.
447472
*
448473
* @param writeMode true if schema is used to write data
474+
* @deprecated Use {@code setReadOnly} or {@code setWriteOnly}
449475
*/
476+
@Deprecated
450477
public void setWriteMode(boolean writeMode) {
451-
this.writeMode = writeMode;
478+
if (writeMode) {
479+
setWriteOnly(true);
480+
} else {
481+
setReadOnly(true);
482+
}
452483
}
453484

454485
/**

src/main/java/com/networknt/schema/ValidatorTypeCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
9393
PREFIX_ITEMS("prefixItems", "1048", PrefixItemsValidator.class, VersionCode.MinV202012),
9494
PROPERTIES("properties", "1025", PropertiesValidator.class, VersionCode.AllVersions),
9595
PROPERTYNAMES("propertyNames", "1044", PropertyNamesValidator.class, VersionCode.MinV6),
96-
READ_ONLY("readOnly", "1032", ReadOnlyValidator.class, VersionCode.AllVersions),
96+
READ_ONLY("readOnly", "1032", ReadOnlyValidator.class, VersionCode.MinV7),
9797
REF("$ref", "1026", RefValidator.class, VersionCode.AllVersions),
9898
REQUIRED("required", "1028", RequiredValidator.class, VersionCode.AllVersions),
9999
TRUE("true", "1040", TrueValidator.class, VersionCode.MinV6),
@@ -103,6 +103,7 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
103103
UNION_TYPE("unionType", "1030", UnionTypeValidator.class, VersionCode.AllVersions),
104104
UNIQUE_ITEMS("uniqueItems", "1031", UniqueItemsValidator.class, VersionCode.AllVersions),
105105
UUID("uuid", "1035", null, VersionCode.AllVersions),
106+
WRITE_ONLY("writeOnly", "1027", WriteOnlyValidator.class, VersionCode.MinV7),
106107
;
107108

108109
private static final Map<String, ValidatorTypeCode> CONSTANTS = new HashMap<>();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.networknt.schema;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import com.fasterxml.jackson.databind.JsonNode;
10+
11+
public class WriteOnlyValidator extends BaseJsonValidator {
12+
private static final Logger logger = LoggerFactory.getLogger(WriteOnlyValidator.class);
13+
14+
private final boolean writeOnly;
15+
16+
public WriteOnlyValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
17+
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.WRITE_ONLY, validationContext);
18+
19+
this.writeOnly = validationContext.getConfig().isWriteOnly();
20+
logger.debug("Loaded WriteOnlyValidator for property {} as {}", parentSchema, "write mode");
21+
parseErrorCode(getValidatorType().getErrorCodeKey());
22+
}
23+
24+
@Override
25+
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
26+
debug(logger, node, rootNode, at);
27+
Set<ValidationMessage> errors= new HashSet<>();
28+
if (this.writeOnly) {
29+
errors.add(buildValidationMessage(at));
30+
}
31+
return errors;
32+
}
33+
34+
}

src/main/resources/jsv-messages.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ unevaluatedProperties = There are unevaluated properties at the following paths
4747
unionType = {0}: {1} found, but {2} is required
4848
uniqueItems = {0}: the items in the array must be unique
4949
uuid = {0}: {1} is an invalid {2}
50+
writeOnly = {0}: is a write-only field, it cannot appear in the data

src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,17 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS
132132
config.setTypeLoose(typeLoose);
133133
config.setEcma262Validator(TestSpec.RegexKind.JDK != testSpec.getRegex());
134134
testSpec.getStrictness().forEach(config::setStrict);
135-
if (testSpec.getConfig() != null && testSpec.getConfig().containsKey("isCustomMessageSupported")) {
136-
config.setCustomMessageSupported((Boolean) testSpec.getConfig().get("isCustomMessageSupported"));
135+
136+
if (testSpec.getConfig() != null) {
137+
if (testSpec.getConfig().containsKey("isCustomMessageSupported")) {
138+
config.setCustomMessageSupported((Boolean) testSpec.getConfig().get("isCustomMessageSupported"));
139+
}
140+
if (testSpec.getConfig().containsKey("readOnly")) {
141+
config.setReadOnly((Boolean) testSpec.getConfig().get("readOnly"));
142+
}
143+
if (testSpec.getConfig().containsKey("writeOnly")) {
144+
config.setWriteOnly((Boolean) testSpec.getConfig().get("writeOnly"));
145+
}
137146
}
138147

139148
URI testCaseFileUri = URI.create("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));

src/test/java/com/networknt/schema/ReadOnlyValidatorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private JsonSchema getJsonSchema(Boolean write) {
4848

4949
private SchemaValidatorsConfig createSchemaConfig(Boolean write) {
5050
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
51-
config.setWriteMode(write);
51+
config.setReadOnly(write);
5252
return config;
5353
}
5454

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[
2+
{
3+
"description": "issue798",
4+
"schema": {
5+
"$schema": "https://json-schema.org/draft/2020-12/schema",
6+
"properties": {
7+
"a": { "type": "string" },
8+
"b": { "type": "string", "readOnly": true },
9+
"c": { "type": "string", "writeOnly": true },
10+
"d": { "type": "string", "readOnly": true, "writeOnly": true }
11+
}
12+
},
13+
"tests": [
14+
{
15+
"description": "default behavior",
16+
"data": { "a": "a string" },
17+
"valid": true,
18+
"config": { "readOnly": true, "writeOnly": true }
19+
},
20+
{
21+
"description": "readonly behavior",
22+
"data": { "a": "a string", "b": "a string" },
23+
"valid": false,
24+
"config": { "readOnly": true, "writeOnly": true },
25+
"validationMessages": [ "$.b: is a readonly field, it cannot be changed" ]
26+
},
27+
{
28+
"description": "write-only behavior",
29+
"data": { "a": "a string", "c": "a string" },
30+
"valid": false,
31+
"config": { "readOnly": true, "writeOnly": true },
32+
"validationMessages": [ "$.c: is a write-only field, it cannot appear in the data" ]
33+
},
34+
{
35+
"description": "both behavior",
36+
"data": { "a": "a string", "d": "a string" },
37+
"valid": false,
38+
"config": { "readOnly": true, "writeOnly": true },
39+
"validationMessages": [
40+
"$.d: is a readonly field, it cannot be changed",
41+
"$.d: is a write-only field, it cannot appear in the data"
42+
]
43+
}
44+
]
45+
}
46+
]

0 commit comments

Comments
 (0)