From 580dfad020cf81c7272bbf2ed7b687b9fcd1402d Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Sat, 19 Dec 2020 13:22:24 -0500 Subject: [PATCH 1/2] fixes #11 upgrade dependency to the latest in 2.0.0 --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a9ce992..e332029 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 com.mservicetech openapi-schema-validation - 1.0.2 + 2.0.0 jar openapi schema for openpai 3.* https://github.com/mservicetech/openapi-schema-validation @@ -57,9 +57,9 @@ 11 UTF-8 1.0.44 - 2.0.16 - 2.10.0 - 1.7.25 + 2.0.22-SNAPSHOT + 2.10.4 + 1.7.30 3.5 1.2.3 4.12 @@ -70,7 +70,7 @@ com.networknt - openapi-meta + openapi-helper ${version.light-4j} From 45c535e177be1a2e442c0bebaa5de451e7c9e62c Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Sat, 19 Dec 2020 14:28:45 -0500 Subject: [PATCH 2/2] fixes #11 refactor using openapi-helper --- pom.xml | 6 + .../openapi/common/HttpStatus.java | 515 ++++++++ .../openapi/common/ParameterType.java | 29 + .../mservicetech/openapi/common/Status.java | 173 +++ .../openapi/validation/ApiNormalisedPath.java | 95 -- .../openapi/validation/OpenApiHelper.java | 118 -- .../openapi/validation/OpenApiValidator.java | 29 +- .../openapi/validation/SchemaValidator.java | 7 +- src/main/resources/status.yml | 1165 +++++++++++++++++ .../validation/OpenApiValidatorTest.java | 4 +- 10 files changed, 1910 insertions(+), 231 deletions(-) create mode 100644 src/main/java/com/mservicetech/openapi/common/HttpStatus.java create mode 100644 src/main/java/com/mservicetech/openapi/common/ParameterType.java create mode 100644 src/main/java/com/mservicetech/openapi/common/Status.java delete mode 100644 src/main/java/com/mservicetech/openapi/validation/ApiNormalisedPath.java delete mode 100644 src/main/java/com/mservicetech/openapi/validation/OpenApiHelper.java create mode 100644 src/main/resources/status.yml diff --git a/pom.xml b/pom.xml index e332029..c93fb80 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ 1.0.44 2.0.22-SNAPSHOT 2.10.4 + 1.26 1.7.30 3.5 1.2.3 @@ -88,6 +89,11 @@ jackson-databind ${version.jackson} + + org.yaml + snakeyaml + ${version.snakeyaml} + org.slf4j slf4j-api diff --git a/src/main/java/com/mservicetech/openapi/common/HttpStatus.java b/src/main/java/com/mservicetech/openapi/common/HttpStatus.java new file mode 100644 index 0000000..971097b --- /dev/null +++ b/src/main/java/com/mservicetech/openapi/common/HttpStatus.java @@ -0,0 +1,515 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mservicetech.openapi.common; + +public enum HttpStatus { + + // 1xx Informational + + /** + * {@code 100 Continue}. + * @see HTTP/1.1: Semantics and Content, section 6.2.1 + */ + CONTINUE(100, "Continue"), + /** + * {@code 101 Switching Protocols}. + * @see HTTP/1.1: Semantics and Content, section 6.2.2 + */ + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + /** + * {@code 102 Processing}. + * @see WebDAV + */ + PROCESSING(102, "Processing"), + /** + * {@code 103 Checkpoint}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, "Checkpoint"), + + // 2xx Success + + /** + * {@code 200 OK}. + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ + OK(200, "OK"), + /** + * {@code 201 Created}. + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ + CREATED(201, "Created"), + /** + * {@code 202 Accepted}. + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ + ACCEPTED(202, "Accepted"), + /** + * {@code 203 Non-Authoritative Information}. + * @see HTTP/1.1: Semantics and Content, section 6.3.4 + */ + NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), + /** + * {@code 204 No Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ + NO_CONTENT(204, "No Content"), + /** + * {@code 205 Reset Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.6 + */ + RESET_CONTENT(205, "Reset Content"), + /** + * {@code 206 Partial Content}. + * @see HTTP/1.1: Range Requests, section 4.1 + */ + PARTIAL_CONTENT(206, "Partial Content"), + /** + * {@code 207 Multi-Status}. + * @see WebDAV + */ + MULTI_STATUS(207, "Multi-Status"), + /** + * {@code 208 Already Reported}. + * @see WebDAV Binding Extensions + */ + ALREADY_REPORTED(208, "Already Reported"), + /** + * {@code 226 IM Used}. + * @see Delta encoding in HTTP + */ + IM_USED(226, "IM Used"), + + // 3xx Redirection + + /** + * {@code 300 Multiple Choices}. + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ + MULTIPLE_CHOICES(300, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ + MOVED_PERMANENTLY(301, "Moved Permanently"), + /** + * {@code 302 Found}. + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ + FOUND(302, "Found"), + + /** + * {@code 303 See Other}. + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ + SEE_OTHER(303, "See Other"), + /** + * {@code 304 Not Modified}. + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ + NOT_MODIFIED(304, "Not Modified"), + + /** + * {@code 307 Temporary Redirect}. + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * @see RFC 7238 + */ + PERMANENT_REDIRECT(308, "Permanent Redirect"), + + // --- 4xx Client Error --- + + /** + * {@code 400 Bad Request}. + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ + BAD_REQUEST(400, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * @see HTTP/1.1: Authentication, section 3.1 + */ + UNAUTHORIZED(401, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ + PAYMENT_REQUIRED(402, "Payment Required"), + /** + * {@code 403 Forbidden}. + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ + FORBIDDEN(403, "Forbidden"), + /** + * {@code 404 Not Found}. + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ + NOT_FOUND(404, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ + NOT_ACCEPTABLE(406, "Not Acceptable"), + /** + * {@code 407 Proxy Authentication Required}. + * @see HTTP/1.1: Authentication, section 3.2 + */ + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), + /** + * {@code 408 Request Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.5.7 + */ + REQUEST_TIMEOUT(408, "Request Timeout"), + /** + * {@code 409 Conflict}. + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ + CONFLICT(409, "Conflict"), + /** + * {@code 410 Gone}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.9 + */ + GONE(410, "Gone"), + /** + * {@code 411 Length Required}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ + LENGTH_REQUIRED(411, "Length Required"), + /** + * {@code 412 Precondition failed}. + * @see + * HTTP/1.1: Conditional Requests, section 4.2 + */ + PRECONDITION_FAILED(412, "Precondition Failed"), + /** + * {@code 413 Payload Too Large}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + */ + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + + /** + * {@code 414 URI Too Long}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + */ + URI_TOO_LONG(414, "URI Too Long"), + + /** + * {@code 415 Unsupported Media Type}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + /** + * {@code 416 Requested Range Not Satisfiable}. + * @see HTTP/1.1: Range Requests, section 4.4 + */ + REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"), + /** + * {@code 417 Expectation Failed}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.14 + */ + EXPECTATION_FAILED(417, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, "I'm a teapot"), + + + /** + * {@code 422 Unprocessable Entity}. + * @see WebDAV + */ + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), + /** + * {@code 423 Locked}. + * @see WebDAV + */ + LOCKED(423, "Locked"), + /** + * {@code 424 Failed Dependency}. + * @see WebDAV + */ + FAILED_DEPENDENCY(424, "Failed Dependency"), + /** + * {@code 425 Too Early}. + * @since 5.2 + * @see RFC 8470 + */ + TOO_EARLY(425, "Too Early"), + /** + * {@code 426 Upgrade Required}. + * @see Upgrading to TLS Within HTTP/1.1 + */ + UPGRADE_REQUIRED(426, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * @see Additional HTTP Status Codes + */ + PRECONDITION_REQUIRED(428, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status Codes + */ + TOO_MANY_REQUESTS(429, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * @see Additional HTTP Status Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), + /** + * {@code 451 Unavailable For Legal Reasons}. + * @see + * An HTTP Status Code to Report Legal Obstacles + * @since 4.3 + */ + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), + + // --- 5xx Server Error --- + + /** + * {@code 500 Internal Server Error}. + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ + NOT_IMPLEMENTED(501, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ + BAD_GATEWAY(502, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ + GATEWAY_TIMEOUT(504, "Gateway Timeout"), + /** + * {@code 505 HTTP Version Not Supported}. + * @see HTTP/1.1: Semantics and Content, section 6.6.6 + */ + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"), + /** + * {@code 506 Variant Also Negotiates} + * @see Transparent Content Negotiation + */ + VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), + /** + * {@code 507 Insufficient Storage} + * @see WebDAV + */ + INSUFFICIENT_STORAGE(507, "Insufficient Storage"), + /** + * {@code 508 Loop Detected} + * @see WebDAV Binding Extensions + */ + LOOP_DETECTED(508, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), + /** + * {@code 510 Not Extended} + * @see HTTP Extension Framework + */ + NOT_EXTENDED(510, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * @see Additional HTTP Status Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + + + private final int value; + + private final String reasonPhrase; + + + HttpStatus(int value, String reasonPhrase) { + this.value = value; + this.reasonPhrase = reasonPhrase; + } + + + /** + * Return the integer value of this status code. + * @return int + */ + public int value() { + return this.value; + } + + /** + * Return the reason phrase of this status code. + * @return String + */ + public String getReasonPhrase() { + return this.reasonPhrase; + } + + public Series series() { + return Series.valueOf(this); + } + + + public boolean is1xxInformational() { + return (series() == Series.INFORMATIONAL); + } + + + public boolean is2xxSuccessful() { + return (series() == Series.SUCCESSFUL); + } + + + public boolean is3xxRedirection() { + return (series() == Series.REDIRECTION); + } + + + public boolean is4xxClientError() { + return (series() == Series.CLIENT_ERROR); + } + + + public boolean is5xxServerError() { + return (series() == Series.SERVER_ERROR); + } + + + public boolean isError() { + return (is4xxClientError() || is5xxServerError()); + } + + /** + * Return a string representation of this status code. + */ + @Override + public String toString() { + return this.value + " " + name(); + } + + + /** + * Return the enum constant of this type with the specified numeric value. + * @param statusCode the numeric value of the enum to be returned + * @return the enum constant with the specified numeric value + * @throws IllegalArgumentException if this enum has no constant for the specified numeric value + */ + public static HttpStatus valueOf(int statusCode) { + HttpStatus status = resolve(statusCode); + if (status == null) { + throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); + } + return status; + } + + + public static HttpStatus resolve(int statusCode) { + for (HttpStatus status : values()) { + if (status.value == statusCode) { + return status; + } + } + return null; + } + + + + public enum Series { + + INFORMATIONAL(1), + SUCCESSFUL(2), + REDIRECTION(3), + CLIENT_ERROR(4), + SERVER_ERROR(5); + + private final int value; + + Series(int value) { + this.value = value; + } + + /** + * Return the integer value of this status series. Ranges from 1 to 5. + * @return int + */ + + public int value() { + return this.value; + } + + /** + * Return the enum constant of this type with the corresponding series. + * @param status a standard HTTP status enum value + * @return the enum constant of this type with the corresponding series + * @throws IllegalArgumentException if this enum has no corresponding constant + */ + public static Series valueOf(HttpStatus status) { + return valueOf(status.value); + } + + /** + * Return the enum constant of this type with the corresponding series. + * @param statusCode the HTTP status code (potentially non-standard) + * @return the enum constant of this type with the corresponding series + * @throws IllegalArgumentException if this enum has no corresponding constant + */ + public static Series valueOf(int statusCode) { + Series series = resolve(statusCode); + if (series == null) { + throw new IllegalArgumentException("No matching constant for [" + statusCode + "]"); + } + return series; + } + + + public static Series resolve(int statusCode) { + int seriesCode = statusCode / 100; + for (Series series : values()) { + if (series.value == seriesCode) { + return series; + } + } + return null; + } + } + +} diff --git a/src/main/java/com/mservicetech/openapi/common/ParameterType.java b/src/main/java/com/mservicetech/openapi/common/ParameterType.java new file mode 100644 index 0000000..1970264 --- /dev/null +++ b/src/main/java/com/mservicetech/openapi/common/ParameterType.java @@ -0,0 +1,29 @@ +package com.mservicetech.openapi.common; + +import com.networknt.utility.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +public enum ParameterType { + PATH, + QUERY, + HEADER, + COOKIE; + + private static Map lookup = new HashMap<>(); + + static { + for (ParameterType type: ParameterType.values()) { + lookup.put(type.name(), type); + } + } + + public static ParameterType of(String typeStr) { + return lookup.get(StringUtils.trimToEmpty(typeStr).toUpperCase()); + } + + public static boolean is(String typeStr, ParameterType type) { + return type == of(typeStr); + } +} diff --git a/src/main/java/com/mservicetech/openapi/common/Status.java b/src/main/java/com/mservicetech/openapi/common/Status.java new file mode 100644 index 0000000..76e0a85 --- /dev/null +++ b/src/main/java/com/mservicetech/openapi/common/Status.java @@ -0,0 +1,173 @@ +package com.mservicetech.openapi.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.IllegalFormatException; +import java.util.Map; +import org.yaml.snakeyaml.Yaml; + +import static java.lang.String.format; + +/** + * For every status response, there is only one message returned. This means the server + * will fail fast and won't return multiple message at all. Two benefits for this design: + *

+ * 1. low latency as server will return the first error without further processing + * 2. limited attack risks and make the error handling harder to analyzed + * + * @author Steve Hu + */ +public class Status { + private static final Logger logger = LoggerFactory.getLogger(Status.class); + static final Map config; + + // default severity + public static final String defaultSeverity = "ERROR"; + + private int statusCode; + private String code; + private String severity; + private String message; + private String description; + + static { + InputStream inputStream = Status.class.getClassLoader().getResourceAsStream("status.yml"); + final Yaml yaml = new Yaml(); + config = yaml.load(inputStream); + } + + /** + * Default construction that is only used in reflection. + */ + public Status() { + } + + /** + * Construct a status object based on error code and a list of arguments. It is + * the most popular way to create status object from status.yml definition. + * + * @param code Error Code + * @param args A list of arguments that will be populated into the error description + */ + public Status(final String code, final Object... args) { + this.code = code; + @SuppressWarnings("unchecked") + Map map = (Map) config.get(code); + if (map != null) { + this.statusCode = (Integer) map.get("statusCode"); + this.message = (String) map.get("message"); + this.description = (String) map.get("description"); + try { + this.description = format(this.description, args); + } catch (IllegalFormatException e) { +// logger.warn(format("Error formatting description of status %s", code), e); + } + if ((this.severity = (String) map.get("severity")) == null) + this.severity = defaultSeverity; + + } + } + + /** + * Construct a status object based on all the properties in the object. It is not + * very often to use this construct to create object. + * + * @param statusCode Status Code + * @param code Code + * @param message Message + * @param description Description + */ + public Status(int statusCode, String code, String message, String description) { + this.statusCode = statusCode; + this.code = code; + this.severity = defaultSeverity; + this.message = message; + this.description = description; + } + + /** + * Construct a status object based on all the properties in the object. It is not + * very often to use this construct to create object. + * + * @param httpStatus HttpStatus + * @param message Message + * @param description Description + */ + public Status(HttpStatus httpStatus, String message, String description) { + this.statusCode = httpStatus.value(); + this.code = httpStatus.getReasonPhrase(); + this.severity = defaultSeverity; + this.message = message; + this.description = description; + } + + + /** + * Construct a status object based on all the properties in the object. It is not + * very often to use this construct to create object. + * + * @param statusCode Status Code + * @param code Code + * @param severity Status Severity + * @param message Message + * @param description Description + */ + public Status(int statusCode, String code, String message, String description, String severity) { + this.statusCode = statusCode; + this.code = code; + this.severity = severity; + this.message = message; + this.description = description; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setSeverity(String severity) { + this.severity = severity; + } + + public String getSeverity() { + return severity; + } + + @Override + public String toString() { + return "{\"statusCode\":" + getStatusCode() + + ",\"code\":\"" + getCode() + + "\",\"message\":\"" + + getMessage() + "\",\"description\":\"" + + getDescription() + "\",\"severity\":\"" + getSeverity() + "\"}"; + } +} diff --git a/src/main/java/com/mservicetech/openapi/validation/ApiNormalisedPath.java b/src/main/java/com/mservicetech/openapi/validation/ApiNormalisedPath.java deleted file mode 100644 index 38807b9..0000000 --- a/src/main/java/com/mservicetech/openapi/validation/ApiNormalisedPath.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2016 Network New Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mservicetech.openapi.validation; - -import com.networknt.oas.model.OpenApi3; -import com.networknt.openapi.NormalisedPath; -import com.networknt.openapi.OpenApiHelper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -import static java.util.Arrays.asList; -import static java.util.Collections.unmodifiableList; -import static java.util.Objects.requireNonNull; - -/** - * This utility normalize the RESTful API path so that they can be - * handled identically. - * - * @author Steve Hu - */ -public class ApiNormalisedPath implements NormalisedPath { - Logger logger = LoggerFactory.getLogger(ApiNormalisedPath.class); - private final List pathParts; - private final String original; - private final String normalised; - - public ApiNormalisedPath(final String path, OpenApi3 openApi3, String basePath) { - if (logger.isDebugEnabled()) logger.debug("path =" + path); - this.original = requireNonNull(path, "A path is required"); - this.normalised = normalise(path, openApi3, basePath); - if (logger.isDebugEnabled()) logger.debug("normalised = " + this.normalised); - this.pathParts = unmodifiableList(asList(normalised.split("/"))); - } - - @Override - public List parts() { - return pathParts; - } - - @Override - public String part(int index) { - return pathParts.get(index); - } - - @Override - public boolean isParam(int index) { - final String part = part(index); - return part.startsWith("{") && part.endsWith("}"); - } - - @Override - public String paramName(int index) { - if (!isParam(index)) { - return null; - } - final String part = part(index); - return part.substring(1, part.length() - 1); - } - - @Override - public String original() { - return original; - } - - @Override - public String normalised() { - return normalised; - } - - private String normalise(String requestPath, OpenApi3 openApi3, String basePath) { - if(openApi3 != null && basePath.length() > 0) { - requestPath = requestPath.replaceFirst(OpenApiHelper.basePath, ""); - } - if (!requestPath.startsWith("/")) { - return "/" + requestPath; - } - return requestPath; - } -} diff --git a/src/main/java/com/mservicetech/openapi/validation/OpenApiHelper.java b/src/main/java/com/mservicetech/openapi/validation/OpenApiHelper.java deleted file mode 100644 index bf53e82..0000000 --- a/src/main/java/com/mservicetech/openapi/validation/OpenApiHelper.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2016 Network New Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mservicetech.openapi.validation; - -import com.mservicetech.openapi.common.OpenApiLoadException; -import com.networknt.oas.OpenApiParser; -import com.networknt.oas.model.OpenApi3; -import com.networknt.oas.model.SecurityScheme; -import com.networknt.oas.model.Server; -import com.networknt.openapi.NormalisedPath; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - - -public class OpenApiHelper { - - - static final Logger logger = LoggerFactory.getLogger(OpenApiHelper.class); - - public final OpenApi3 openApi3; - public final List oauth2Names; - public final String basePath; - - - - public OpenApiHelper(String spec) throws OpenApiLoadException { - try { - openApi3 = (OpenApi3) new OpenApiParser().parse(spec, new URL("https://oas.lightapi.net/")); - oauth2Names = getOAuth2Name(); - basePath = getBasePath(); - } catch (Exception e) { - throw new OpenApiLoadException(e.getMessage()); - } - - } - - public Optional findMatchingApiPath(final NormalisedPath requestPath) { - if(this.openApi3 != null) { - return this.openApi3.getPaths().keySet() - .stream() - .map(p -> (NormalisedPath) new ApiNormalisedPath(p, openApi3, basePath)) - .filter(p -> pathMatches(requestPath, p)) - .findFirst(); - } else { - return Optional.empty(); - } - } - - protected List getOAuth2Name() { - List names = new ArrayList<>(); - Map defMap = openApi3.getSecuritySchemes(); - if(defMap != null) { - for(Map.Entry entry : defMap.entrySet()) { - if(entry.getValue().getType().equals("oauth2")) { - names.add(entry.getKey()); - } - } - } - return names; - } - - protected String getBasePath() { - - String basePath = ""; - String url = null; - if (openApi3.getServers().size() > 0) { - Server server = openApi3.getServer(0); - url = server.getUrl(); - } - if (url != null) { - // find "://" index - int protocolIndex = url.indexOf("://"); - int pathIndex = url.indexOf('/', protocolIndex + 3); - if (pathIndex > 0) { - basePath = url.substring(pathIndex); - } - } - return basePath; - } - - private boolean pathMatches(final NormalisedPath requestPath, final NormalisedPath apiPath) { - if (requestPath.parts().size() != apiPath.parts().size()) { - return false; - } - for (int i = 0; i < requestPath.parts().size(); i++) { - if (requestPath.part(i).equalsIgnoreCase(apiPath.part(i)) || apiPath.isParam(i)) { - continue; - } - return false; - } - return true; - } - - public OpenApi3 getOpenApi3() { - return openApi3; - } - -} diff --git a/src/main/java/com/mservicetech/openapi/validation/OpenApiValidator.java b/src/main/java/com/mservicetech/openapi/validation/OpenApiValidator.java index 763bedc..96efb2f 100644 --- a/src/main/java/com/mservicetech/openapi/validation/OpenApiValidator.java +++ b/src/main/java/com/mservicetech/openapi/validation/OpenApiValidator.java @@ -1,8 +1,10 @@ package com.mservicetech.openapi.validation; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mservicetech.openapi.common.ParameterType; import com.mservicetech.openapi.common.RequestEntity; -import com.networknt.config.Config; +import com.mservicetech.openapi.common.Status; import com.networknt.jsonoverlay.Overlay; import com.networknt.oas.model.Operation; import com.networknt.oas.model.Parameter; @@ -10,11 +12,11 @@ import com.networknt.oas.model.RequestBody; import com.networknt.oas.model.impl.RequestBodyImpl; import com.networknt.oas.model.impl.SchemaImpl; +import com.networknt.openapi.ApiNormalisedPath; import com.networknt.openapi.NormalisedPath; +import com.networknt.openapi.OpenApiHelper; import com.networknt.openapi.OpenApiOperation; -import com.networknt.openapi.parameter.ParameterType; import com.networknt.schema.SchemaValidatorsConfig; -import com.networknt.status.Status; import com.networknt.utility.StringUtils; import org.slf4j.Logger; @@ -41,6 +43,7 @@ public class OpenApiValidator { public String spec; public OpenApiHelper openApiHelper; public SchemaValidator schemaValidator; + private ObjectMapper objectMapper = new ObjectMapper(); /** * Construct a new request validator with the given schema validator. @@ -54,8 +57,8 @@ public OpenApiValidator() { throw new IOException("cannot load openapi spec file"); } spec = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")); - openApiHelper = new OpenApiHelper(spec); - schemaValidator = new SchemaValidator(openApiHelper.getOpenApi3()); + openApiHelper = OpenApiHelper.init(spec); + schemaValidator = new SchemaValidator(openApiHelper.openApi3); } catch (Exception e) { logger.error("initial failed:" + e); } @@ -69,8 +72,8 @@ public OpenApiValidator() { public OpenApiValidator(String openapiPath) { InputStream in = this.getClass().getClassLoader().getResourceAsStream(openapiPath); spec = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")); - openApiHelper = new OpenApiHelper(spec); - schemaValidator = new SchemaValidator(openApiHelper.getOpenApi3()); + openApiHelper = OpenApiHelper.init(spec); + schemaValidator = new SchemaValidator(openApiHelper.openApi3); } /** @@ -80,8 +83,8 @@ public OpenApiValidator(String openapiPath) { */ public OpenApiValidator(InputStream openapi) { spec = new BufferedReader(new InputStreamReader(openapi, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")); - openApiHelper = new OpenApiHelper(spec); - schemaValidator = new SchemaValidator(openApiHelper.getOpenApi3()); + openApiHelper = OpenApiHelper.init(spec); + schemaValidator = new SchemaValidator(openApiHelper.openApi3); } /** @@ -93,7 +96,7 @@ public OpenApiValidator(InputStream openapi) { */ public Status validateRequestPath (String requestURI , String httpMethod, RequestEntity requestEntity ) { requireNonNull(openApiHelper, "openApiHelper object cannot be null"); - final NormalisedPath requestPath = new ApiNormalisedPath(requestURI, openApiHelper.getOpenApi3(), openApiHelper.getBasePath()); + final NormalisedPath requestPath = new ApiNormalisedPath(requestURI); final Optional maybeApiPath = openApiHelper.findMatchingApiPath(requestPath); if (!maybeApiPath.isPresent()) { Status status = new Status( STATUS_INVALID_REQUEST_PATH, requestPath.normalised()); @@ -101,7 +104,7 @@ public Status validateRequestPath (String requestURI , String httpMethod, Reques } final NormalisedPath openApiPathString = maybeApiPath.get(); - final Path path = openApiHelper.getOpenApi3().getPath(openApiPathString.original()); + final Path path = openApiHelper.openApi3.getPath(openApiPathString.original()); final Operation operation = path.getOperation(httpMethod.toLowerCase()); OpenApiOperation openApiOperation = new OpenApiOperation(openApiPathString, path, httpMethod, operation); @@ -331,10 +334,10 @@ private Object attachJsonBody( String bodyString) throws Exception { if (bodyString != null) { bodyString = bodyString.trim(); if (bodyString.startsWith("{")) { - body = Config.getInstance().getMapper().readValue(bodyString, new TypeReference>() { + body = objectMapper.readValue(bodyString, new TypeReference>() { }); } else if (bodyString.startsWith("[")) { - body = Config.getInstance().getMapper().readValue(bodyString, new TypeReference>() { + body = objectMapper.readValue(bodyString, new TypeReference>() { }); } else { // error here. The content type in head doesn't match the body. diff --git a/src/main/java/com/mservicetech/openapi/validation/SchemaValidator.java b/src/main/java/com/mservicetech/openapi/validation/SchemaValidator.java index f3878eb..504bf74 100644 --- a/src/main/java/com/mservicetech/openapi/validation/SchemaValidator.java +++ b/src/main/java/com/mservicetech/openapi/validation/SchemaValidator.java @@ -17,8 +17,8 @@ package com.mservicetech.openapi.validation; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.networknt.config.Config; import com.networknt.jsonoverlay.Overlay; import com.networknt.oas.model.OpenApi3; import com.networknt.oas.model.impl.OpenApi3Impl; @@ -26,7 +26,7 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.ValidationMessage; -import com.networknt.status.Status; +import com.mservicetech.openapi.common.Status; import java.util.Set; @@ -47,6 +47,7 @@ public class SchemaValidator { private final OpenApi3 api; private JsonNode jsonNode; private final SchemaValidatorsConfig defaultConfig; + private final ObjectMapper objectMapper = new ObjectMapper(); /** * Build a new validator with no API specification. @@ -115,7 +116,7 @@ private Status doValidate(final Object value, final JsonNode schema, SchemaValid ((ObjectNode)schema).set(COMPONENTS_FIELD, jsonNode); } JsonSchema jsonSchema = JsonSchemaFactory.getInstance().getSchema(schema, config); - final JsonNode content = Config.getInstance().getMapper().valueToTree(value); + final JsonNode content = objectMapper.valueToTree(value); processingReport = jsonSchema.validate(content, content, at); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/resources/status.yml b/src/main/resources/status.yml new file mode 100644 index 0000000..37a6b15 --- /dev/null +++ b/src/main/resources/status.yml @@ -0,0 +1,1165 @@ +--- +SUC10200: + statusCode: 200 + code: SUC10200 + message: OK + severity: NA + description: The request is handled successfully +SUC10201: + statusCode: 201 + code: SUC10201 + message: CREATED + severity: NA + description: A new resource is created +SUC10202: + statusCode: 202 + code: SUC10202 + message: ACCEPTED + severity: NA + description: The request has been accepted for processing +SUC10203: + statusCode: 203 + code: SUC10203 + message: NON-AUTHORITATIVE_INFORMATION + severity: NA + description: The server received a 200 OK but returned a modified version of response +SUC10204: + statusCode: 204 + code: SUC10204 + message: NO_CONTENT + severity: NA + description: The request is processed but no conent is returned +SUC10205: + statusCode: 205 + code: SUC10205 + message: RESET_CONTENT + severity: NA + description: The request is processed but no conent is returned and document view needs to be reset +SUC10206: + statusCode: 206 + code: SUC10206 + message: PARTIAL_CONTENT + severity: NA + description: The server is delivering only part of the resource +SUC10207: + statusCode: 207 + code: SUC10207 + message: MULTI-STATUS + severity: NA + description: Separate response codes are returned +SUC10208: + statusCode: 208 + code: SUC10208 + message: ALREADY_REPORTED + severity: NA + description: DAV bindings have been returned in a preceding response +SUC10209: + statusCode: 209 + code: SUC10209 + message: ALREADY_REPORTED + severity: NA + description: DAV bindings have been returned in a preceding response +SUC10226: + statusCode: 226 + code: SUC10226 + message: IM_USED + severity: NA + description: The response is a representation of the result of one or more instance-manipulations applied to the current instance + +SUC10300: + statusCode: 300 + code: SUC10300 + message: MULTIPLE_CHOICES + severity: NA + description: The client can choose multiple options for the resource +SUC10301: + statusCode: 301 + code: SUC10301 + message: MOVED_PERMANENTLY + severity: NA + description: All future requests should be redirect to the give URI +SUC10302: + statusCode: 302 + code: SUC10302 + message: FOUND + severity: NA + description: Please look at (browse to) another url +SUC10303: + statusCode: 303 + code: SUC10303 + message: SEE_OTHER + severity: NA + description: The request can be found under another URI +SUC10304: + statusCode: 304 + code: SUC10304 + message: NOT_MODIFIED + severity: NA + description: The resource has not been modified +SUC10305: + statusCode: 305 + code: SUC10305 + message: USE_PROXY + severity: NA + description: The resource is available only through a proxy +SUC10306: + statusCode: 306 + code: SUC10306 + message: SWITCH_PROXY + severity: NA + description: Subsequent requests should use a specified proxy +SUC10307: + statusCode: 307 + code: SUC10307 + message: TEMPORARY_REDIRECT + severity: NA + description: The request should be repeated with another URI +SUC10308: + statusCode: 308 + code: SUC10308 + message: PERMANENT_REDIRECT + severity: NA + description: The request and all future requests should be repeated with another URI + +# 10000-11000 Light Java Framework validation errors +ERR10000: + statusCode: 401 + code: ERR10000 + message: INVALID_AUTH_TOKEN + description: Incorrect signature or malformed token in authorization header +ERR10001: + statusCode: 401 + code: ERR10001 + message: AUTH_TOKEN_EXPIRED + description: Jwt token in authorization header expired +ERR10002: + statusCode: 401 + code: ERR10002 + message: MISSING_AUTH_TOKEN + description: No Authorization header or the token is not bearer type +ERR10003: + statusCode: 401 + code: ERR10003 + message: INVALID_SCOPE_TOKEN + description: Incorrect signature or malformed token in X-Scope-Token header +ERR10004: + statusCode: 401 + code: ERR10004 + message: SCOPE_TOKEN_EXPIRED + description: Jwt token in X-Scope-Token expired +ERR10005: + statusCode: 403 + code: ERR10005 + message: AUTH_TOKEN_SCOPE_MISMATCH + description: Scopes %s in authorization token and specification scopes %s are not matched +ERR10006: + statusCode: 403 + code: ERR10006 + message: SCOPE_TOKEN_SCOPE_MISMATCH + description: Scopes %s in scope token and specification scopes %s are not matched +ERR10007: + statusCode: 404 + code: ERR10007 + message: INVALID_REQUEST_PATH + description: Request path %s cannot be found in the spec. +ERR10008: + statusCode: 405 + code: ERR10008 + message: METHOD_NOT_ALLOWED + description: Request method cannot be found in the spec. +ERR10009: + statusCode: 408 + code: ERR10009 + message: CLIENT_CREDENTIALS_TOKEN_NOT_AVAILABLE + description: Could not get client credentials token in client module +ERR10010: + statusCode: 500 + code: ERR10010 + message: RUNTIME_EXCEPTION + description: Unexpected runtime exception +ERR10011: + statusCode: 400 + code: ERR10011 + message: UNCAUGHT_EXCEPTION + description: Uncaught exception +ERR10012: + statusCode: 400 + code: ERR10012 + message: MISSING_SWAGGER_OPERATION + description: SwaggerHandler or JwtVerifyHandler needs to be enabled +ERR10013: + statusCode: 404 + code: ERR10013 + message: SERVER_INFO_DISABLED + description: Server info handler is disabled in info.json +ERR10014: + statusCode: 400 + code: ERR10014 + message: GENERIC_EXCEPTION + description: Generic Exception %s. +ERR10015: + statusCode: 400 + code: ERR10015 + message: CONTENT_TYPE_MISMATCH + description: Content type in header %s doesn't match the body. The body might be trucated due to bufferSize to small in client.xml +ERR10016: + statusCode: 400 + code: ERR10016 + message: MISSING_HANDlER + description: Could not find the handler for the endpoint %s. +ERR10017: + statusCode: 400 + code: ERR10017 + message: SQL_EXCEPTION + description: Sql Exception %s. +ERR10018: + statusCode: 500 + code: ERR10018 + message: CREATE_REGISTRY_ERROR + description: Failed to create registry for url %s +ERR10019: + statusCode: 500 + code: ERR10019 + message: PARSE_DIRECT_URL_ERROR + description: parse direct url error, invalid direct registry address %s, address should be ip1:port1,ip2:port2 ... +ERR10020: + statusCode: 500 + code: ERR10020 + message: REGISTER_ERROR + description: Class %s fails to register %s to %s +ERR10021: + statusCode: 500 + code: ERR10021 + message: UNREGISTER_ERROR + description: Class %s fails to unregister %s to %s +ERR10022: + statusCode: 500 + code: ERR10022 + message: SUBSCRIBE_ERROR + description: Class %s fails to subscribe %s to %s +ERR10023: + statusCode: 500 + code: ERR10023 + message: UNSUBSCRIBE_ERROR + description: Class %s fails to unsubscribe %s to %s +ERR10024: + statusCode: 500 + code: ERR10024 + message: REGISTRY_IS_NULL + description: Registry must not be null +ERR10025: + statusCode: 500 + code: ERR10025 + message: WEIGHT_OUT_OF_RANGE + description: Weight must be integer between 1 and 100 but the actual value is %s +ERR10026: + statusCode: 500 + code: ERR10026 + message: GET_NODETYPEPATH_ERROR + description: Failed to get nodeTypePath, url %s type %s +ERR10027: + statusCode: 500 + code: ERR10027 + message: SUBSCRIBE_ZOOKEEPER_SERVICE_ERROR + description: Failed to subscribe service %s to zookeeper(%s), cause %s +ERR10028: + statusCode: 500 + code: ERR10028 + message: SUBSCRIBE_ZOOKEEPER_COMMAND_ERROR + description: Failed to subscribe command %s to zookeeper(%s), cause %s +ERR10029: + statusCode: 500 + code: ERR10029 + message: UNSUBSCRIBE_ZOOKEEPER_SERVICE_ERROR + description: Failed to unsubscribe service %s to zookeeper(%s), cause %s +ERR10030: + statusCode: 500 + code: ERR10030 + message: UNSUBSCRIBE_ZOOKEEPER_COMMAND_ERROR + description: Failed to unsubscribe command %s to zookeeper(%s), cause %s +ERR10031: + statusCode: 500 + code: ERR10031 + message: DISCOVER_ZOOKEEPER_SERVICE_ERROR + description: Failed to discover service %s from zookeeper(%s), cause %s +ERR10032: + statusCode: 500 + code: ERR10032 + message: DISCOVER_ZOOKEEPER_COMMAND_ERROR + description: Failed to discover command %s from zookeeper(%s), cause %s +ERR10033: + statusCode: 500 + code: ERR10033 + message: REGISTER_ZOOKEEPER_ERROR + description: Failed to register %s to zookeeper(%s), cause %s +ERR10034: + statusCode: 500 + code: ERR10034 + message: UNREGISTER_ZOOKEEPER_ERROR + description: Failed to unregister %s to zookeeper(%s), cause %s +ERR10035: + statusCode: 400 + code: ERR10035 + message: AUTHORIZATION_CODE_MISSING + description: Authorization code is missing when calling AuthHandler +ERR10036: + statusCode: 400 + code: ERR10036 + message: CSRF_HEADER_MISSING + description: X-CSRF-TOKEN header is missing in Middleware CsrfHandler +ERR10037: + statusCode: 400 + code: ERR10037 + message: REFRESH_TOKEN_RESPONSE_EMPTY + description: Empty response returned from OAuth 2.0 provider while using refresh token to get access token +ERR10038: + statusCode: 400 + code: ERR10038 + message: CSRF_TOKEN_MISSING_IN_JWT + description: CSRF token is missing in JWT token while StatelessAuthHandler is enabled +ERR10039: + statusCode: 400 + code: ERR10039 + message: HEADER_CSRF_JWT_CSRF_NOT_MATCH + description: CSRF token %s in header is not matched with CSRF token %s in JWT +ERR10040: + statusCode: 400 + code: ERR10040 + message: JWT_NOT_FOUND_IN_COOKIES + description: JWT token cannot be found in cookies set by StatelessAuthHandler +ERR10041: + statusCode: 400 + code: ERR10041 + message: AUDIT_INFO_NOT_FOUND + description: AuditInfo could not be found in exchange attachment. Ensure that OpenApiHandler or SwaggerHandler is used +ERR10042: + statusCode: 400 + code: ERR10042 + message: ERROR_NOT_DEFINED + description: Error code %s is not defined in status.yml file +ERR10043: + statusCode: 400 + code: ERR10043 + message: CLIENT_AUTHENTICATE_CLASS_NOT_FOUND + description: Authenticate class %s defined for the client could not be found +ERR10044: + statusCode: 400 + code: ERR10044 + message: EMPTY_TOKEN_DEREFERENCE_RESPONSE + description: OAuth 2.0 provider returns empty response with de-reference token request for token %s +ERR10045: + statusCode: 400 + code: ERR10045 + message: TOKEN_DEREFERENCE_ERROR + description: Token de-reference from OAuth 2.0 provider returns error %s +ERR10046: + statusCode: 401 + code: ERR10046 + message: INVALID_BASIC_HEADER + description: Invalid basic authentication header %s +ERR10047: + statusCode: 401 + code: ERR10047 + message: INVALID_USERNAME_OR_PASSWORD + description: Invalid username or password for basic authentication +ERR10048: + statusCode: 404 + code: ERR10048 + message: PATH_NOT_IMPLEMENTED + description: No handler defined for path '%s' +ERR10049: + statusCode: 403 + code: ERR10049 + message: INVALID_IP_FOR_PATH + description: Peer IP %s is not in the whitelist for the endpoint %s +ERR10050: + statusCode: 406 + code: ERR10050 + message: NO_ENCODING_HANDLER + description: No encoding handler for the required encoder. +ERR10051: + statusCode: 401 + code: ERR10051 + message: FAIL_TO_SEND_REQUEST + description: Fail to send request to target server. +ERR10052: + statusCode: 401 + code: ERR10052 + message: GET_TOKEN_ERROR + description: Cannot get valid token %s. +ERR10053: + statusCode: 401 + code: ERR10053 + message: ESTABLISH_CONNECTION_ERROR + description: Cannot establish connection for url %s. +ERR10054: + statusCode: 401 + code: ERR10054 + message: GET_TOKEN_TIMEOUT + description: Cannot get valid token, probably due to a timeout. +ERR10055: + statusCode: 400 + code: ERR10055 + message: TLS_TRUSTSTORE_ERROR + description: Cannot load client.truststore to create HttpClient. +ERR10056: + statusCode: 400 + code: ERR10056 + message: OAUTH_SERVER_URL_ERROR + description: Neither server_url nor serviceId is configured in client.yml for %s. +ERR10057: + statusCode: 400 + code: ERR10057 + message: CONFIG_PROPERTY_MISSING + description: Config property %s is missing in config file %s. +ERR10058: + statusCode: 400 + code: ERR10058 + message: ERROR_CONNECT_REGISTRY + description: Failed to connect to the registry %s. +ERR10059: + statusCode: 400 + code: ERR10059 + message: REQUEST_BODY_MISSING + description: Request body is missing. +ERR10060: + statusCode: 400 + code: ERR10060 + message: CONFIG_VALUE_INVALID + description: Config value %s for key %s invalid in config file %s. + +# 11000-11500 swagger-validator errors +ERR11000: + statusCode: 400 + code: ERR11000 + message: VALIDATOR_REQUEST_PARAMETER_QUERY_MISSING + description: Query parameter %s is required on path %s but not found in request. +ERR11001: + statusCode: 400 + code: ERR11001 + message: VALIDATOR_REQUEST_PARAMETER_MISSING + description: Parameter %s is required but is missing. +ERR11002: + statusCode: 400 + code: ERR11002 + message: VALIDATOR_REQUEST_PARAMETER_ENUM_INVALID + description: Value %s for parameter %s is not allowed. Allowed values are <%s>. +ERR11003: + statusCode: 400 + code: ERR11003 + message: VALIDATOR_SCHEMA_INVALID_JSON + description: Unable to parse JSON - %s +ERR11004: + statusCode: 400 + code: ERR11004 + message: VALIDATOR_SCHEMA + description: Schema Validation Error - %s +ERR11005: + statusCode: 400 + code: ERR11005 + message: VALIDATOR_REQUEST_PARAMETER_COLLECTION_INVALID_FORMAT + description: Parameter %s expected collection format of %s but %s was used instead. +ERR11006: + statusCode: 400 + code: ERR11006 + message: VALIDATOR_REQUEST_PARAMETER_COLLECTION_TOO_MANY_ITEMS + description: Parameter %s accepts a maximum of %d items. Found %d. +ERR11007: + statusCode: 400 + code: ERR11007 + message: VALIDATOR_REQUEST_PARAMETER_COLLECTION_TOO_FEW_ITEMS + description: Parameter %s accepts a minimum of %d items. Found %d. +ERR11008: + statusCode: 400 + code: ERR11008 + message: VALIDATOR_REQUEST_PARAMETER_COLLECTION_DUPLICATE_ITEMS + description: Parameter %s does not allow duplicate values. +ERR11009: + statusCode: 400 + code: ERR11009 + message: VALIDATOR_REQUEST_PARAMETER_ENUM_INVALID + description: Value %s for parameter %s is not allowed. Allowed values are <%s>. +ERR11010: + statusCode: 400 + code: ERR11010 + message: VALIDATOR_REQUEST_PARAMETER_INVALID_FORMAT + description: Value %s for parameter %s does not match type %s with format %s. +ERR11011: + statusCode: 400 + code: ERR11011 + message: VALIDATOR_REQUEST_PARAMETER_NUMBER_BELOW_MIN + description: Value %s for parameter %s less than allowed min value %f. +ERR11012: + statusCode: 400 + code: ERR11012 + message: VALIDATOR_REQUEST_PARAMETER_NUMBER_ABOVE_MAX + description: Value %s for parameter %s greater than allowed max value %f. +ERR11013: + statusCode: 400 + code: ERR11013 + message: VALIDATOR_REQUEST_BODY_UNEXPECTED + description: No request body is expected for %s on path %s. +ERR11014: + statusCode: 400 + code: ERR11014 + message: VALIDATOR_REQUEST_BODY_MISSING + description: Method %s on path %s requires a request body. None found. +ERR11015: + statusCode: 400 + code: ERR11015 + message: VALIDATOR_RESPONSE_STATUS_UNKNOWN + description: Response status %d not defined for path %s. +ERR11016: + statusCode: 400 + code: ERR11016 + message: VALIDATOR_RESPONSE_BODY_MISSING + description: Method %s on path %s defines a response schema but no response body found. +ERR11017: + statusCode: 400 + code: ERR11017 + message: VALIDATOR_REQUEST_PARAMETER_HEADER_MISSING + description: Header parameter %s is required on path %s but not found in request. +ERR11018: + statusCode: 400 + code: ERR11018 + message: VALIDATOR_RESPONSE_CONTENT_UNEXPECTED + description: No response body content or schema is expected for %s on path %s. + +# 11100-11199 Light Code Generator validation errors +ERR11100: + statusCode: 400 + code: ERR11100 + message: INVALID_FRAMEWORK + description: Invalid framework %s. +ERR11101: + statusCode: 400 + code: ERR11101 + message: MISSING_GENERATOR_ITEM + description: Missing generator item. +ERR11102: + statusCode: 400 + code: ERR11102 + message: INVALID_CONFIG_HTTP_LISTEN_TYPE + description: enableHttp or enableHttps needs to be set to true, but not both. +ERR11103: + statusCode: 400 + code: ERR11103 + message: INVALID_MODEL_URL + description: Invalid model URL %s. +ERR11104: + statusCode: 400 + code: ERR11104 + message: INVALID_CONFIG_JSON + description: Invalid config JSON format. +ERR11105: + statusCode: 400 + code: ERR11105 + message: INVALID_CONFIG_URL_EXTENSION + description: Invalid config URL extension %s. +ERR11106: + statusCode: 400 + code: ERR11106 + message: GENERATOR_EXCEPTION + description: Exception occurs during generation %s. +ERR11107: + statusCode: 400 + code: ERR11107 + message: COMPRESSION_EXCEPTION + description: Exception occurs during compression %s. + + +# 11200 - 11299 light hybrid validation errors +ERR11200: + statusCode: 400 + code: ERR11200 + message: HANDLER_NOT_FOUND + description: Handler for service %s cannot be found. +ERR11201: + statusCode: 400 + code: ERR11201 + message: REQUEST_BODY_EMPTY + description: Hybrid request body is empty for post method. + +ERR11202: + statusCode: 400 + code: ERR11202 + message: REQUEST_CMD_EMPTY + description: Hybrid request cmd parameter is empty for get method. + +# 11300-11349 light-proxy + +ERR11300: + statusCode: 400 + code: ERR11300 + message: FAIL_TO_GET_TABLEAU_TOKEN + description: Fail to get token from Tableau server. +ERR11301: + statusCode: 400 + code: ERR11301 + message: MISSING_TABLEAU_CONTENT_URL + description: TableauContentUrl is missing in the request header. + + +# 11350-11399 light-router +ERR11350: + statusCode: 400 + code: ERR11350 + message: EMAIL_REGISTERED + description: Email %s has been registered with a different userId %s. + +# 11400-11499 light-config-server +ERR11400: + statusCode: 400 + code: ERR11400 + message: CONFIG_SERVER_INITIALIZED + description: The config server has been initialized already with an encryption key. +ERR11401: + statusCode: 400 + code: ERR11401 + message: INVALID_INITIALIZE_KEY_FORMAT + description: Invalid initialize key format for light-config-server. +ERR11402: + statusCode: 400 + code: ERR11402 + message: ERROR_WRITING_KEY_FILE + description: Error writing key file to path %s. + +# 11500 - 11600 GraphQL valdator errors +ERR11500: + statusCode: 400 + code: ERR11500 + message: GRAHPQL_INVALID_PATH + description: Invalid GraphQL path %s, must be %s. +ERR11501: + statusCode: 405 + code: ERR11501 + message: GRAHPQL_INVALID_METHOD + description: Invalid GraphQL method %s, only GET and POST are allowed. +ERR11502: + statusCode: 400 + code: ERR11502 + message: GRAHPQL_MISSING_QUERY + description: Missing GraphQL query. + +# 11601 - 11999 light-portal errors +ERR11601: + statusCode: 400 + code: ERR11601 + message: INCORRECT_TOKEN_TYPE + description: Incorrect token type. Expecting %s. +ERR11602: + statusCode: 400 + code: ERR11602 + message: DELETE_USER_EXCEPTION + description: Exception %s is caught when deleting user %s. +ERR11603: + statusCode: 400 + code: ERR11603 + message: DELETE_USER_DENIED + description: Delete user %s denied with user %s and roles %s. +ERR11604: + statusCode: 400 + code: ERR11604 + message: USER_ID_IS_USED + description: UserId %s is used already. +ERR11605: + statusCode: 400 + code: ERR11605 + message: SEND_MESSAGE_EXCEPITON + description: Send message exception caused by %s with user %s. +ERR11606: + statusCode: 400 + code: ERR11606 + message: UPDATE_USER_EXCEPTION + description: Exception %s is caught when updating user %s. +ERR11607: + statusCode: 404 + code: ERR11607 + message: USER_NOT_FOUND_BY_EMAIL + description: User not found by email %s. +ERR11608: + statusCode: 400 + code: ERR11608 + message: CHANGE_PASSWORD_DENIED + description: User %s is not allowed to change password for %s. +ERR11609: + statusCode: 404 + code: ERR11609 + message: USER_NOT_FOUND_BY_ID + description: User not found by userId %s. +ERR11610: + statusCode: 400 + code: ERR11610 + message: EMAIL_NOT_CONFIRMED + description: Email %s is not confirmed. Please find the user confirmation email. +ERR11611: + statusCode: 400 + code: ERR11611 + message: USER_EMAIL_IS_USED + description: User email address %s is registered. +ERR11612: + statusCode: 400 + code: ERR11612 + message: WRONG_LOGIN_PASSWORD + description: Wrong login password for user with email %s. +ERR11613: + statusCode: 400 + code: ERR11613 + message: PASSWORD_NOT_MATCH + description: Password and passwordConfirm are not matched. +ERR11614: + statusCode: 400 + code: ERR11614 + message: ERROR_HASHING_PASSWORD + description: Error in hashing password with exception %s. +ERR11615: + statusCode: 400 + code: ERR11615 + message: ERROR_PARSING_USER + description: Error in parsing user to JSON with exception %s. +ERR11616: + statusCode: 400 + code: ERR11616 + message: ERROR_VALIDATE_PASSWORD + description: Password validation error with excetion %s. +ERR11617: + statusCode: 400 + code: ERR11617 + message: USER_NOT_ACTIVATED + description: User %s is not activated. +ERR11618: + statusCode: 400 + code: ERR11618 + message: USER_QUERY_DENIED + description: User %s is not matched the JWT user_id %s and not admin role. +ERR11619: + statusCode: 400 + code: ERR11619 + message: EMAIL_SENDING_ERROR + description: Failed to send the confirmation email to %s. +ERR11620: + statusCode: 400 + code: ERR11620 + message: PERMISSION_DENIED + description: Fail to access the endpoint for user or roles %s. +ERR11621: + statusCode: 400 + code: ERR11621 + message: CITY_REGISTERED + description: The city has been registered with key %s, %s, %s. +ERR11622: + statusCode: 400 + code: ERR11622 + message: PROFILE_LOCATION_INCOMPLETE + description: Country, Province or City is missing in user profile. +ERR11623: + statusCode: 404 + code: ERR11623 + message: CITY_NOT_FOUND + description: City not found for %s, %s, %s. +ERR11624: + statusCode: 400 + code: ERR11624 + message: COUNTRY_PROVINCE_CITY_EMPTY + description: Country %s or province %s or city %s is empty. +ERR11625: + statusCode: 404 + code: ERR11625 + message: ENTITY_NOT_FOUND + description: Entity not found for %s at %s, %s and %s defined in your profile. Please create an entity first. +ERR11626: + statusCode: 404 + code: ERR11626 + message: STATUS_NOT_FOUND + description: Status not found for email or userId %s. +ERR11627: + statusCode: 404 + code: ERR11627 + message: PRIVATE_MESSAGE_NOT_FOUND + description: Private message not found for email %s. +ERR11628: + statusCode: 404 + code: ERR11628 + message: WEBSITE_NOT_FOUND + description: Website not found for email %s. +ERR11629: + statusCode: 400 + code: ERR11629 + message: WALLET_QUERY_DENIED + description: Taiji Wallet %s email is not matched the JWT user_id %s and not admin role. +ERR11630: + statusCode: 404 + code: ERR11630 + message: EMAIL_NOT_FOUND_BY_WALLET + description: Email not found for Taiji wallet %s. +ERR11631: + statusCode: 404 + code: ERR11631 + message: NO_ENTITY_REGISTERED + description: No entity has been registered for the %s, %s, %s, %s, %s combination. +ERR11632: + statusCode: 404 + code: ERR11632 + message: PAYMENT_NOT_FOUND_BY_EMAIL + description: Payment not found for email %s. +ERR11633: + statusCode: 404 + code: ERR11633 + message: ORDER_NOT_FOUND_BY_EMAIL + description: Order not found for email %s. +ERR11634: + statusCode: 404 + code: ERR11634 + message: REFERENCE_NOT_FOUND + description: Reference not found for name %s and language %s. +ERR11635: + statusCode: 400 + code: ERR11635 + message: HOST_NOT_MATCH_EMAIL + description: Host %s doesn't match the email domain %s. +ERR11636: + statusCode: 400 + code: ERR11636 + message: SQL_EXCEPTION + description: SQLException with error %s. +ERR11637: + statusCode: 404 + code: ERR11637 + message: OBJECT_NOT_FOUND + description: Object %s not found for key %s. +ERR11638: + statusCode: 400 + code: ERR11638 + message: FILESYSTEM_ERROR + description: Error while access filesystem with file %s and error message %s. + + +# OAuth2 service error 12000-13000 + +ERR12000: + statusCode: 400 + code: ERR12000 + message: UNABLE_TO_PARSE_FORM_DATA + description: Unable to parse x-www-form-urlencoded form data. +ERR12001: + statusCode: 400 + code: ERR12001 + message: UNSUPPORTED_GRANT_TYPE + description: Unsupported grant type %s. Only authorization_code and client_credentials + are supported. +ERR12002: + statusCode: 401 + code: ERR12002 + message: MISSING_AUTHORIZATION_HEADER + description: Missing authorization header. client credentials must be passed in + as Authorization header. +ERR12003: + statusCode: 401 + code: ERR12003 + message: INVALID_AUTHORIZATION_HEADER + description: Invalid authorization header %s. Basic authentication with credentials + is required. +ERR12004: + statusCode: 401 + code: ERR12004 + message: INVALID_BASIC_CREDENTIALS + description: Invalid Basic credentials %s. +ERR12005: + statusCode: 400 + code: ERR12005 + message: JSON_PROCESSING_EXCEPTION + description: Json Processing Excepiton %s. +ERR12007: + statusCode: 401 + code: ERR12007 + message: UNAUTHORIZED_CLIENT + description: Unauthorized client with wrong client secret. +ERR12008: + statusCode: 401 + code: ERR12008 + message: INVALID_AUTHORIZATION_CODE + description: Invalid Authorization Code %s. +ERR12009: + statusCode: 401 + code: ERR12009 + message: INVALID_CODE_REQUEST + description: Invalid authorization code request. responseType and client_id are required. +ERR12011: + statusCode: 400 + code: ERR12011 + message: PASSWORD_OR_PASSWORDCONFIRM_EMPTY + description: Password %s or PasswordConfirm %s is empty. +ERR12012: + statusCode: 400 + code: ERR12012 + message: PASSWORD_PASSWORDCONFIRM_NOT_MATCH + description: Password %s and PasswordConfirm %s are not matched. +ERR12013: + statusCode: 404 + code: ERR12013 + message: USER_NOT_FOUND + description: User %s is not found. +ERR12014: + statusCode: 404 + code: ERR12014 + message: CLIENT_NOT_FOUND + description: Client %s is not found. +ERR12015: + statusCode: 404 + code: ERR12015 + message: SERVICE_NOT_FOUND + description: Service %s is not found. +ERR12016: + statusCode: 401 + code: ERR12016 + message: INCORRECT_PASSWORD + description: Incorrect password. +ERR12017: + statusCode: 404 + code: ERR12017 + message: KEY_NOT_FOUND + description: Public key certificate keyId %s is not found. +ERR12018: + statusCode: 400 + code: ERR12018 + message: SERVICE_ID_EXISTS + description: Service id %s exists. +ERR12019: + statusCode: 400 + code: ERR12019 + message: CLIENT_ID_EXISTS + description: Client id %s exists. +ERR12020: + statusCode: 400 + code: ERR12020 + message: USER_ID_EXISTS + description: User id %s exists. +ERR12021: + statusCode: 400 + code: ERR12021 + message: EMAIL_EXISTS + description: Email %s exists. +ERR12022: + statusCode: 400 + code: ERR12022 + message: USERNAME_REQUIRED + description: Username is required for password grant type. +ERR12023: + statusCode: 400 + code: ERR12023 + message: PASSWORD_REQUIRED + description: Password is required for password grant type. +ERR12024: + statusCode: 400 + code: ERR12024 + message: NOT_TRUSTED_CLIENT + description: Grant type is allowed only for trusted client type. +ERR12025: + statusCode: 400 + code: ERR12025 + message: MISSING_REDIRECT_URI + description: Missing redirect_uri in authorization code token service as redirect_uri %s is passed in code service. +ERR12026: + statusCode: 400 + code: ERR12026 + message: MISMATCH_REDIRECT_URI + description: redirect_uri %s from token service is not identical as redirect_uri %s from code service. +ERR12027: + statusCode: 400 + code: ERR12027 + message: MISMATCH_SCOPE + description: scope %s does not match previous scope or defined scope for client %s +ERR12028: + statusCode: 400 + code: ERR12028 + message: MISMATCH_CLIENT_ID + description: client_id %s does not match previous client_id %s in refresh_token request. +ERR12029: + statusCode: 404 + code: ERR12029 + message: REFRESH_TOKEN_NOT_FOUND + description: Refresh token %s is not found. +ERR12030: + statusCode: 400 + code: ERR12030 + message: INVALID_KEY_ID + description: Invalid key id %s. + +ERR12031: + statusCode: 400 + code: ERR12031 + message: USER_ID_REQUIRED_FOR_CLIENT_AUTHENTICATED_USER_GRANT_TYPE + description: UserId is required for client_authenticated_user grant type. + +ERR12032: + statusCode: 400 + code: ERR12032 + message: USER_TYPE_REQUIRED_FOR_CLIENT_AUTHENTICATED_USER_GRANT_TYPE + description: UserType is required for client_authenticated_user grant type. + +ERR12033: + statusCode: 400 + code: ERR12033 + message: INVALID_CODE_CHALLENGE_METHOD + description: Invalid PKCE code challenge method %s. + +ERR12034: + statusCode: 400 + code: ERR12034 + message: CODE_CHALLENGE_TOO_SHORT + description: PKCE codeChallenge length under lower limit, codeChallenge = %s +ERR12035: + statusCode: 400 + code: ERR12035 + message: CODE_CHALLENGE_TOO_LONG + description: PKCE codeChallenge length over upper limit, codeChallenge = %s +ERR12036: + statusCode: 400 + code: ERR12036 + message: INVALID_CODE_CHALLENGE_FORMAT + description: PKCE codeChallenge format is invalid, codeChallenge = %s +ERR12037: + statusCode: 400 + code: ERR12037 + message: INVALID_CODE_VERIFIER_FORMAT + description: PKCE codeVerifier format is invalid, codeVerifier = %s +ERR12038: + statusCode: 400 + code: ERR12038 + message: CODE_VERIFIER_TOO_SHORT + description: PKCE codeVerifier length under lower limit , codeVerifier = %s +ERR12039: + statusCode: 400 + code: ERR12039 + message: CODE_VERIFIER_TOO_LONG + description: PKCE codeVerifier length over upper limit , codeVerifier = %s +ERR12040: + statusCode: 400 + code: ERR12040 + message: CODE_VERIFIER_MISSING + description: PKCE codeVerifier is not specified +ERR12041: + statusCode: 400 + code: ERR12041 + message: CODE_VERIFIER_FAILED + description: PKCE verification failed + +ERR12042: + statusCode: 404 + code: ERR12042 + message: SERVICE_ENDPOINT_NOT_FOUND + description: Service endpoint not found for serviceId %s + +ERR12043: + statusCode: 400 + code: ERR12043 + message: DEREF_NOT_EXTERNAL + description: Only external client type needs optional deref client id +ERR12044: + statusCode: 400 + code: ERR12044 + message: DEREF_CLIENT_NOT_MATCH + description: The authenticated client id %s is not matched to the registered dereference client id +ERR12045: + statusCode: 400 + code: ERR12045 + message: DEREF_TOKEN_NOT_FOUND + description: The reference token %s cannot be found to exchange to JWT +ERR12046: + statusCode: 400 + code: ERR12046 + message: JWT_TOKEN_NOT_FOUND + description: The jwt token cannot be found to match the reference token %s +ERR12047: + statusCode: 400 + code: ERR12047 + message: PROVIDER_ID_NOT_EXISTING + description: The provider id is not existing +ERR12048: + statusCode: 400 + code: ERR12048 + message: PROVIDER_ID_EXISTS + description: Provider id %s exists; It has been regristed already +ERR12049: + statusCode: 400 + code: ERR12049 + message: PROVIDER_ID_INVALID + description: Provider id invalid +ERR12050: + statusCode: 400 + code: ERR12050 + message: FAILED_TO_POPULATE_HEADER + description: Failed to populate client credentials scope token in header in method %s +ERR12051: + statusCode: 400 + code: ERR12051 + message: HOST_IS_REQUIRED + description: Host is required + +# Tokenization service +ERR12100: + statusCode: 404 + code: ERR12100 + message: SCHEME_NOT_FOUND + description: Tokenizer scheme %s not found +ERR12101: + statusCode: 400 + code: ERR12101 + message: CLIENT_ID_MISSING + description: Client_id is missing, JWT verification needs to be enable in security.yml +ERR12102: + statusCode: 400 + code: ERR12102 + message: DATABASE_NOT_FOUND + description: Database not found from client_id %s to database mapping table. +ERR12103: + statusCode: 400 + code: ERR12103 + message: DATASOURCE_NOT_CONFIGURED + description: DataSource %s is not configured in datasource.yml +ERR12104: + statusCode: 400 + code: ERR12104 + message: FAIL_TO_QUERY_TOKEN_VAULT + description: Fail to query token_vault table in database %s +ERR12105: + statusCode: 400 + code: ERR12105 + message: FAIL_TO_INSERT_TOKEN_VAULT + description: Fail to insert into token_vault table in database %s +ERR12106: + statusCode: 400 + code: ERR12106 + message: FAIL_TO_DELETE_TOKEN_VAULT + description: Fail to delete from token_vault table in database %s +ERR12107: + statusCode: 404 + code: ERR12107 + message: TOKEN_NOT_FOUND + description: Token %s not found in token_vault table in database %s +ERR12108: + statusCode: 400 + code: ERR12108 + message: LOGGING_DISABLED + description: Logging is disabled in logging.yml +ERR12109: + statusCode: 400 + code: ERR12109 + message: LOGGING_LEVEL_NOT_PROVIDED + description: Logging level is not provided + +# http2client error 13000-14000 +ERR13001: + statusCode: 403 + code: ERR13001 + message: UNABLE_PARSE_SCOPES_FROM_HEADER + description: Cannot parse scopes from request headers. + +# 20000-29999 common error codes within your business domain. +# It is highly recommended to have your error code shared within your organization or your line of business. +# In that case, all your services can share the same status.yml which in org default config + + +# 90000-99999 customize error code that cannot be found in common range. +# Only use this section if you cannot make your error code into 20000-29999 range on time. +# In this case, you have to overwrite your status.yml per service. diff --git a/src/test/java/com/mservicetech/openapi/validation/OpenApiValidatorTest.java b/src/test/java/com/mservicetech/openapi/validation/OpenApiValidatorTest.java index da5d34b..a6372b6 100644 --- a/src/test/java/com/mservicetech/openapi/validation/OpenApiValidatorTest.java +++ b/src/test/java/com/mservicetech/openapi/validation/OpenApiValidatorTest.java @@ -2,7 +2,7 @@ import com.mservicetech.openapi.common.RequestEntity; -import com.networknt.status.Status; +import com.mservicetech.openapi.common.Status; import org.junit.Assert; import org.junit.BeforeClass; @@ -28,7 +28,7 @@ public static void setUp() { @Test public void testFileLoad() { - Assert.assertNotNull(openApiValidator.openApiHelper.getOpenApi3()); + Assert.assertNotNull(openApiValidator.openApiHelper.openApi3); Assert.assertNotNull(openApiValidator.openApiHelper.basePath); }