View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2023 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.siteapi.openapi.validator;
21  
22  import java.util.stream.Collectors;
23  
24  import org.jetbrains.annotations.NotNull;
25  import org.openapi4j.core.util.TreeUtil;
26  import org.openapi4j.core.validation.ValidationResults.ValidationItem;
27  import org.openapi4j.schema.validator.ValidationData;
28  import org.openapi4j.schema.validator.v3.SchemaValidator;
29  
30  import com.fasterxml.jackson.core.JsonProcessingException;
31  import com.fasterxml.jackson.databind.JsonNode;
32  
33  /**
34   * Validates JSON response for a given path definition and suffix against the JSON.
35   * Create instance via {@link OpenApiSpec} class.
36   */
37  public final class OpenApiSchemaValidator {
38  
39    private final String suffix;
40    private final SchemaValidator schemaValidator;
41  
42    OpenApiSchemaValidator(@NotNull String suffix, @NotNull SchemaValidator schemaValidator) {
43      this.suffix = suffix;
44      this.schemaValidator = schemaValidator;
45    }
46  
47    /**
48     * @return Suffix
49     */
50    public @NotNull String getSuffix() {
51      return this.suffix;
52    }
53  
54    /**
55     * Validate the given JSON response against the operation's JSON schema.
56     * @param jsonValue JSON response
57     * @throws ContentValidationException Validation failed
58     */
59    public void validate(@NotNull String jsonValue) throws ContentValidationException {
60      JsonNode node = readJson(jsonValue);
61      validateAgainstSchema(node);
62    }
63  
64    private JsonNode readJson(@NotNull String jsonValue) throws ContentValidationException {
65      try {
66        return TreeUtil.json.readTree(jsonValue);
67      }
68      catch (JsonProcessingException ex) {
69        throw new ContentValidationException("Unable to parse JSON:\n" + jsonValue, ex);
70      }
71    }
72  
73    private void validateAgainstSchema(@NotNull JsonNode node) throws ContentValidationException {
74      ValidationData<Void> validation = new ValidationData<>();
75      schemaValidator.validate(node, validation);
76      if (validation.isValid()) {
77        return;
78      }
79      String message = "JSON invalid for suffix '" + suffix + "': " + validation.results().items().stream()
80          .map(ValidationItem::toString)
81          .collect(Collectors.joining("\n"));
82      throw new ContentValidationException(message);
83    }
84  
85  }