1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.api.jackson;
18
19 import static org.forgerock.api.jackson.JacksonUtils.OBJECT_MAPPER;
20 import static org.forgerock.json.JsonValue.json;
21
22 import java.io.IOException;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.regex.Pattern;
28
29 import javax.validation.ValidationException;
30
31 import org.forgerock.api.enums.ReadPolicy;
32 import org.forgerock.api.enums.WritePolicy;
33 import org.forgerock.json.JsonValue;
34
35 import com.fasterxml.jackson.annotation.JsonProperty;
36 import com.fasterxml.jackson.databind.JavaType;
37 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
38 import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
39
40
41
42
43 public class CrestObjectSchema extends ObjectSchema implements CrestReadWritePoliciesSchema, OrderedFieldSchema,
44 ValidatableSchema, RequiredFieldsSchema, WithExampleSchema<Map<String, Object>> {
45 private static final JavaType EXAMPLE_VALUE_TYPE = OBJECT_MAPPER.getTypeFactory()
46 .constructParametrizedType(HashMap.class, Map.class, String.class, Object.class);
47 private WritePolicy writePolicy;
48 private ReadPolicy readPolicy;
49 private Boolean errorOnWritePolicyFailure;
50 private Boolean returnOnDemand;
51 private Integer propertyOrder;
52 private Set<String> requiredFields;
53 private Map<String, Object> example;
54
55 @Override
56 public WritePolicy getWritePolicy() {
57 return writePolicy;
58 }
59
60 @Override
61 public void setWritePolicy(WritePolicy policy) {
62 this.writePolicy = policy;
63 }
64
65 @Override
66 public ReadPolicy getReadPolicy() {
67 return readPolicy;
68 }
69
70 @Override
71 public void setReadPolicy(ReadPolicy readPolicy) {
72 this.readPolicy = readPolicy;
73 }
74
75 @Override
76 public Boolean getErrorOnWritePolicyFailure() {
77 return errorOnWritePolicyFailure;
78 }
79
80 @Override
81 public void setErrorOnWritePolicyFailure(Boolean errorOnWritePolicyFailure) {
82 this.errorOnWritePolicyFailure = errorOnWritePolicyFailure;
83 }
84
85 @Override
86 public Boolean getReturnOnDemand() {
87 return returnOnDemand;
88 }
89
90 @Override
91 public void setReturnOnDemand(Boolean returnOnDemand) {
92 this.returnOnDemand = returnOnDemand;
93 }
94
95 @Override
96 public Integer getPropertyOrder() {
97 return propertyOrder;
98 }
99
100 @Override
101 public void setPropertyOrder(Integer order) {
102 this.propertyOrder = order;
103 }
104
105 @Override
106 public void validate(JsonValue object) throws ValidationException {
107 if (!object.isMap()) {
108 throw new ValidationException("Object expected, but got: " + object.getObject());
109 }
110 Map<String, Object> propertyValues = new HashMap<>((Map<String, Object>) object.getObject());
111 Iterator<Map.Entry<String, Object>> propertyIterator = propertyValues.entrySet().iterator();
112 Map<Pattern, JsonSchema> patternProperties = new HashMap<>();
113 if (getPatternProperties() != null) {
114 for (Map.Entry<String, JsonSchema> pattern : getPatternProperties().entrySet()) {
115 patternProperties.put(Pattern.compile(pattern.getKey()), pattern.getValue());
116 }
117 }
118 while (propertyIterator.hasNext()) {
119 Map.Entry<String, Object> property = propertyIterator.next();
120 boolean validated = false;
121 if (getProperties().containsKey(property.getKey())) {
122 final JsonSchema schema = getProperties().get(property.getKey());
123 if (schema instanceof ValidatableSchema && !"reference".equals(property.getKey())) {
124 ((ValidatableSchema) schema).validate(json(property.getValue()));
125 }
126 validated = true;
127 }
128 for (Map.Entry<Pattern, JsonSchema> pattern : patternProperties.entrySet()) {
129 if (pattern.getKey().matcher(property.getKey()).matches()) {
130 if (pattern.getValue() instanceof ValidatableSchema) {
131 ((ValidatableSchema) pattern.getValue()).validate(json(property.getValue()));
132 }
133 validated = true;
134 }
135 }
136 if (validated) {
137 propertyIterator.remove();
138 }
139 }
140 AdditionalProperties additionalProperties = getAdditionalProperties();
141 if (additionalProperties != null) {
142 if (additionalProperties instanceof NoAdditionalProperties && !propertyValues.isEmpty()) {
143 throw new ValidationException("Did not expect additional properties, but got " + propertyValues);
144 }
145 SchemaAdditionalProperties schemaAdditionalProperties = (SchemaAdditionalProperties) additionalProperties;
146 if (schemaAdditionalProperties.getJsonSchema() instanceof ValidatableSchema) {
147 final ValidatableSchema schema = (ValidatableSchema) schemaAdditionalProperties.getJsonSchema();
148 for (Object value : propertyValues.values()) {
149 schema.validate(json(value));
150 }
151 }
152 }
153 }
154
155
156
157
158
159
160
161 @JsonProperty("readOnly")
162 @Override
163 public Boolean getReadonly() {
164 return super.getReadonly();
165 }
166
167
168 @JsonProperty("required")
169 @Override
170 public Set<String> getRequiredFields() {
171 return requiredFields;
172 }
173
174 @Override
175 public void setRequiredFields(Set<String> requiredFields) {
176 this.requiredFields = requiredFields;
177 }
178
179 @Override
180 public Map<String, Object> getExample() {
181 Map<String, Object> example = this.example;
182 if (example == null) {
183 example = new HashMap<>();
184 for (Map.Entry<String, JsonSchema> property : getProperties().entrySet()) {
185 if (property.getValue() instanceof WithExampleSchema) {
186 Object propertyExample = ((WithExampleSchema) property.getValue()).getExample();
187 if (propertyExample != null) {
188 example.put(property.getKey(), propertyExample);
189 }
190 }
191 }
192 if (example.isEmpty()) {
193 example = null;
194 }
195 }
196 return example;
197 }
198
199 @Override
200 public void setExample(String example) throws IOException {
201 this.example = OBJECT_MAPPER.readValue(example, EXAMPLE_VALUE_TYPE);
202 }
203 }