diff --git a/domino-rest-client/pom.xml b/domino-rest-client/pom.xml index f00b87d..c712a49 100644 --- a/domino-rest-client/pom.xml +++ b/domino-rest-client/pom.xml @@ -5,7 +5,7 @@ domino-rest org.dominokit - 2.0.0-RC1 + 2.0.0-RC2 4.0.0 @@ -84,7 +84,6 @@ jakarta.ws.rs-api ${jax.rs.version} - org.dominokit domino-rest-jaxrs diff --git a/domino-rest-client/src/main/java/org/dominokit/rest/DominoRestConfig.java b/domino-rest-client/src/main/java/org/dominokit/rest/DominoRestConfig.java index c7204a6..86217f9 100644 --- a/domino-rest-client/src/main/java/org/dominokit/rest/DominoRestConfig.java +++ b/domino-rest-client/src/main/java/org/dominokit/rest/DominoRestConfig.java @@ -19,7 +19,9 @@ import static java.util.Objects.nonNull; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.dominokit.jackson.JacksonContextProvider; import org.dominokit.rest.js.DefaultServiceRoot; import org.dominokit.rest.js.ServerEventFactory; @@ -69,6 +71,10 @@ public class DominoRestConfig implements RestConfig { private static NullQueryParamStrategy nullQueryParamStrategy = NullQueryParamStrategy.EMPTY; + private static final Map globalPathParams = new HashMap<>(); + private static final Map globalHeaderParams = new HashMap<>(); + private static final Map> globalQueryParams = new HashMap<>(); + /** * Gets and initialize the instance with the default configurations * @@ -270,4 +276,78 @@ public UrlTokenRegexMatcher getUrlTokenRegexMatcher() { return url; }; } + + @Override + public Map getGlobalPathParameters() { + return globalPathParams; + } + + @Override + public Map getGlobalHeaderParameters() { + return globalHeaderParams; + } + + @Override + public Map> getGlobalQueryParameters() { + return globalQueryParams; + } + + @Override + public RestConfig setGlobalPathParameter(String name, String value) { + getGlobalPathParameters().put(name, value); + return this; + } + + @Override + public RestConfig setGlobalPathParameters(Map pathParameters) { + getGlobalPathParameters().putAll(pathParameters); + return this; + } + + @Override + public RestConfig setGlobalHeaderParameter(String name, String value) { + getGlobalHeaderParameters().put(name, value); + return this; + } + + @Override + public RestConfig setGlobalHeaderParameters(Map headerParameters) { + getGlobalHeaderParameters().putAll(headerParameters); + return this; + } + + @Override + public RestConfig setGlobalQueryParameter(String name, String value) { + getGlobalQueryParameters().put(name, new ArrayList<>()); + addGlobalQueryParameter(name, value); + return this; + } + + @Override + public RestConfig addGlobalQueryParameter(String name, String value) { + if (getGlobalQueryParameters().containsKey(name)) { + getGlobalQueryParameters().get(name).add(value); + } else { + setGlobalQueryParameter(name, value); + } + return this; + } + + @Override + public RestConfig setGlobalQueryParameters(Map> parameters) { + parameters + .keySet() + .forEach( + name -> parameters.get(name).forEach(value -> addGlobalQueryParameter(name, value))); + return this; + } + + @Override + public RestConfig addGlobalQueryParameters(Map> parameters) { + parameters.forEach( + (key, values) -> { + values.forEach(value -> addGlobalQueryParameter(key, value)); + }); + return this; + } } diff --git a/domino-rest-jaxrs/pom.xml b/domino-rest-jaxrs/pom.xml index 453126a..9defd34 100644 --- a/domino-rest-jaxrs/pom.xml +++ b/domino-rest-jaxrs/pom.xml @@ -5,7 +5,7 @@ org.dominokit domino-rest - 2.0.0-RC1 + 2.0.0-RC2 domino-rest-jaxrs diff --git a/domino-rest-jvm/pom.xml b/domino-rest-jvm/pom.xml index e65e0c5..291f6a0 100644 --- a/domino-rest-jvm/pom.xml +++ b/domino-rest-jvm/pom.xml @@ -5,7 +5,7 @@ domino-rest org.dominokit - 2.0.0-RC1 + 2.0.0-RC2 4.0.0 diff --git a/domino-rest-jvm/src/main/java/org/dominokit/rest/DominoRestConfig.java b/domino-rest-jvm/src/main/java/org/dominokit/rest/DominoRestConfig.java index eaab975..d6d96aa 100644 --- a/domino-rest-jvm/src/main/java/org/dominokit/rest/DominoRestConfig.java +++ b/domino-rest-jvm/src/main/java/org/dominokit/rest/DominoRestConfig.java @@ -20,7 +20,9 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -70,6 +72,10 @@ public class DominoRestConfig implements RestConfig { private static NullQueryParamStrategy nullQueryParamStrategy = NullQueryParamStrategy.EMPTY; + private static final Map globalPathParams = new HashMap<>(); + private static final Map globalHeaderParams = new HashMap<>(); + private static final Map> globalQueryParams = new HashMap<>(); + /** * Gets and initialize the instance with the default configurations * @@ -270,4 +276,78 @@ public UrlTokenRegexMatcher getUrlTokenRegexMatcher() { return url; }; } + + @Override + public Map getGlobalPathParameters() { + return globalPathParams; + } + + @Override + public Map getGlobalHeaderParameters() { + return globalHeaderParams; + } + + @Override + public Map> getGlobalQueryParameters() { + return globalQueryParams; + } + + @Override + public RestConfig setGlobalPathParameter(String name, String value) { + getGlobalPathParameters().put(name, value); + return this; + } + + @Override + public RestConfig setGlobalPathParameters(Map pathParameters) { + getGlobalPathParameters().putAll(pathParameters); + return this; + } + + @Override + public RestConfig setGlobalHeaderParameter(String name, String value) { + getGlobalHeaderParameters().put(name, value); + return this; + } + + @Override + public RestConfig setGlobalHeaderParameters(Map headerParameters) { + getGlobalHeaderParameters().putAll(headerParameters); + return this; + } + + @Override + public RestConfig setGlobalQueryParameter(String name, String value) { + getGlobalQueryParameters().put(name, new ArrayList<>()); + addGlobalQueryParameter(name, value); + return this; + } + + @Override + public RestConfig addGlobalQueryParameter(String name, String value) { + if (getGlobalQueryParameters().containsKey(name)) { + getGlobalQueryParameters().get(name).add(value); + } else { + setGlobalQueryParameter(name, value); + } + return this; + } + + @Override + public RestConfig setGlobalQueryParameters(Map> parameters) { + parameters + .keySet() + .forEach( + name -> parameters.get(name).forEach(value -> addGlobalQueryParameter(name, value))); + return this; + } + + @Override + public RestConfig addGlobalQueryParameters(Map> parameters) { + parameters.forEach( + (key, values) -> { + values.forEach(value -> addGlobalQueryParameter(key, value)); + }); + return this; + } } diff --git a/domino-rest-processor/pom.xml b/domino-rest-processor/pom.xml index 47d1871..44375c0 100644 --- a/domino-rest-processor/pom.xml +++ b/domino-rest-processor/pom.xml @@ -4,7 +4,7 @@ domino-rest org.dominokit - 2.0.0-RC1 + 2.0.0-RC2 4.0.0 diff --git a/domino-rest-processor/src/main/java/org/dominokit/rest/processor/RequestFactorySourceWriter.java b/domino-rest-processor/src/main/java/org/dominokit/rest/processor/RequestFactorySourceWriter.java index b1c86c7..11bc147 100644 --- a/domino-rest-processor/src/main/java/org/dominokit/rest/processor/RequestFactorySourceWriter.java +++ b/domino-rest-processor/src/main/java/org/dominokit/rest/processor/RequestFactorySourceWriter.java @@ -48,6 +48,7 @@ import org.dominokit.jackson.processor.deserialization.FieldDeserializersChainBuilder; import org.dominokit.jackson.processor.serialization.FieldSerializerChainBuilder; import org.dominokit.rest.shared.MultipartForm; +import org.dominokit.rest.shared.Response; import org.dominokit.rest.shared.request.*; import org.dominokit.rest.shared.request.service.annotations.*; import org.dominokit.rest.shared.request.service.annotations.Request; @@ -668,6 +669,10 @@ private TypeMirror getMappingType(TypeMirror returnType) { return elements.getTypeElement(Void.class.getCanonicalName()).asType(); } + if (processorUtil.isAssignableFrom(returnType, jakarta.ws.rs.core.Response.class)) { + return elements.getTypeElement(Response.class.getCanonicalName()).asType(); + } + if (Type.isArray(returnType)) { return returnType; } @@ -959,6 +964,12 @@ private Optional getResponseReader(ServiceMethod serviceMethod) { "setResponseReader(response -> new $T().read(response))", TypeName.get(readerType))); return Optional.of(builder.build()); + } else if (isGenericResponse(serviceMethod)) { + + builder.addStatement( + "setResponseReader(response -> new $T().read(response))", + TypeName.get(GeneralResponseReader.class)); + return Optional.of(builder.build()); } else if (producesJson(serviceMethod)) { TypeMirror responseBeanType = getResponseBeanType(serviceMethod); @@ -997,6 +1008,10 @@ private Optional getResponseReader(ServiceMethod serviceMethod) { return Optional.empty(); } + private boolean isGenericResponse(ServiceMethod serviceMethod) { + return processorUtil.isAssignableFrom(getResponseBeanType(serviceMethod), Response.class); + } + private boolean producesJson(ServiceMethod serviceMethod) { String acceptResponse = getAcceptResponse(serviceMethod); return acceptResponse.contains(MediaType.APPLICATION_JSON) diff --git a/domino-rest-shared/pom.xml b/domino-rest-shared/pom.xml index 64681eb..a7bffaf 100644 --- a/domino-rest-shared/pom.xml +++ b/domino-rest-shared/pom.xml @@ -5,7 +5,7 @@ domino-rest org.dominokit - 2.0.0-RC1 + 2.0.0-RC2 4.0.0 @@ -33,6 +33,7 @@ org.dominokit domino-rest-jaxrs ${project.version} + provided @@ -45,11 +46,6 @@ domino-aggregator-shared ${domino.aggregator.version} - - org.dominokit - domino-history-shared - ${domino.history.version} - javax.annotation javax.annotation-api diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/GeneralResponseReader.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/GeneralResponseReader.java new file mode 100644 index 0000000..d99d8ab --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/GeneralResponseReader.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request; + +import org.dominokit.rest.shared.Response; + +/** Reads the response body as a {@link String} */ +public class GeneralResponseReader implements ResponseReader { + @Override + public Response read(Response response) { + return response; + } +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/RestConfig.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/RestConfig.java index 85e7dc1..da3917b 100644 --- a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/RestConfig.java +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/RestConfig.java @@ -17,6 +17,7 @@ import java.util.Date; import java.util.List; +import java.util.Map; import org.dominokit.rest.shared.request.service.annotations.DateFormat; /** The global configurations for domino rest. */ @@ -176,6 +177,28 @@ default NullQueryParamStrategy getNullQueryParamStrategy() { UrlTokenRegexMatcher getUrlTokenRegexMatcher(); + Map getGlobalPathParameters(); + + Map getGlobalHeaderParameters(); + + Map> getGlobalQueryParameters(); + + RestConfig setGlobalPathParameter(String name, String value); + + RestConfig setGlobalPathParameters(Map pathParameters); + + RestConfig setGlobalHeaderParameter(String name, String value); + + RestConfig setGlobalHeaderParameters(Map headerParameters); + + RestConfig setGlobalQueryParameter(String name, String value); + + RestConfig addGlobalQueryParameter(String name, String value); + + RestConfig setGlobalQueryParameters(Map> parameters); + + RestConfig addGlobalQueryParameters(Map> parameters); + /** Formatter to format the date parameter based on a patter */ @FunctionalInterface interface DateParamFormatter { diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServerRequest.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServerRequest.java index 4488f91..681fb2e 100644 --- a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServerRequest.java +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServerRequest.java @@ -314,7 +314,10 @@ public void normalizeUrl() { (isNull(this.serviceRoot) || this.serviceRoot.isEmpty()) ? ServiceRootMatcher.matchedServiceRoot(path) : (this.serviceRoot + path); - UrlFormatter urlFormatter = new UrlFormatter(pathParameters); + Map combinedParams = new HashMap<>(); + combinedParams.putAll(DominoRestContext.make().getConfig().getGlobalPathParameters()); + combinedParams.putAll(pathParameters); + UrlFormatter urlFormatter = new UrlFormatter(combinedParams); this.setUrl(urlFormatter.formatUrl(root)); } } diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServicePath.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServicePath.java new file mode 100644 index 0000000..2782754 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/ServicePath.java @@ -0,0 +1,394 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ServicePath { + + private static final String QUERY_REGEX = "\\?"; + private static final String FRAGMENT_REGEX = "\\#"; + private final String rootPath; + private List paths = new LinkedList<>(); + private List queryParameters = new LinkedList<>(); + private List fragments = new LinkedList<>(); + + /** @param token String, a URL token */ + public ServicePath(String token) { + this("", token); + } + + /** + * @param rootPath String, the root path token + * @param token String, a URL token + */ + public ServicePath(String rootPath, String token) { + if (isNull(token)) throw new IllegalArgumentException(); + this.rootPath = isNull(rootPath) ? "" : rootPath.trim(); + String rebasedToken = rebaseToken(rootPath, token); + this.paths.addAll(asPathsList(rebasedToken)); + this.queryParameters.addAll(asQueryParameters(rebasedToken)); + this.fragments.addAll(parseFragments(rebasedToken)); + } + + private String rebaseToken(String rootPath, String token) { + if (isNull(rootPath) || rootPath.trim().isEmpty() || !token.startsWith(rootPath)) { + return token; + } + return token.substring(rootPath.length()); + } + + private List parseFragments(String token) { + if (token.contains("#") && token.indexOf("#") < token.length() - 1) + return asPathsList(token.split(FRAGMENT_REGEX)[1]); + return new LinkedList<>(); + } + + /** + * @param path The path to check for + * @return true if the path part of the url ends with the specified path otherwise returns + * false + */ + public boolean endsWithPath(String path) { + if (isEmpty(path)) return false; + return endsWith(paths(), asPathsList(path)); + } + + private boolean endsWith(List paths, List targets) { + if (isValidSize(paths, targets)) return matchEnds(paths, targets); + return false; + } + + private boolean matchEnds(List paths, List targets) { + int offset = paths.size() - targets.size(); + return IntStream.range(0, targets.size()) + .allMatch(i -> targets.get(i).equals(paths.get(i + offset))); + } + + /** + * @return a list of Strings representing all paths of a url, e.g + * http://localhost:8080/a/b/c will return a list contains a, b, + * c, + */ + public List paths() { + return paths; + } + + /** + * @return a list of Strings representing all fragments of a url, e.g + * http://localhost:8080/a/b/c#d/e/f will return a list contains d, d, + * f, + */ + public List fragments() { + return fragments; + } + + /** + * @return the path part of a url, e.g http://localhost:8080/a/b/c will return a/b/c + */ + public String path() { + return String.join("/", paths()); + } + + /** @return the string representing the whole query part of a token */ + public String query() { + return queryParameters.stream().map(Parameter::asQueryString).collect(Collectors.joining("&")); + } + + /** + * @param name name of the query parameter + * @return True if the token has a query param that has the specified name, otherwise + * returns false. + */ + public boolean hasQueryParameter(String name) { + Optional param = + queryParameters.stream().filter(parameter -> parameter.key.equals(name)).findFirst(); + + if (param.isPresent()) { + return true; + } else { + return false; + } + } + + /** + * Adds a query parameter with specified name and value to the current token, if a query parameter + * with same name already exists, then replaces its value with the new one + * + * @param name query parameter name + * @param value query parameter value + * @return {@link ServicePath} that contains the new query parameter. + */ + public ServicePath setQueryParameter(String name, String value) { + if (hasQueryParameter(name)) { + removeParameter(name); + } + appendParameter(name, value); + return this; + } + + /** {@inheritDoc} */ + public ServicePath setQueryParameter(String name, List values) { + if (hasQueryParameter(name)) { + removeParameter(name); + } + appendParameter(name, values); + return this; + } + + private Parameter getParameter(String name) { + Optional param = + queryParameters.stream().filter(parameter -> parameter.key.equals(name)).findFirst(); + + if (param.isPresent()) { + return param.get(); + } else { + return null; + } + } + + /** + * Appends a new query parameter to the end of the token query parameters part. + * + * @param name of the query parameter + * @param value of the query parameter + * @return {@link ServicePath} with the new query parameter appended to the end of query part. + */ + public ServicePath appendParameter(String name, String value) { + return appendParameter(name, asList(value)); + } + + /** {@inheritDoc} */ + public ServicePath appendParameter(String name, List values) { + if (nonNull(name) && !name.trim().isEmpty()) { + if (hasQueryParameter(name)) { + getParameter(name).addValues(values); + } else { + this.queryParameters.add(new Parameter(name, values)); + } + } + return this; + } + + /** + * Replaces the first occurrence of a path segment with the replacement + * + * @param path The path segment to be replaced + * @param replacement the new path segment + * @return {@link ServicePath} with path segment replaced by the replacement + */ + public ServicePath replacePath(String path, String replacement) { + List paths = asPathsList(path()); + if (paths.contains(path)) { + int i = paths.indexOf(path); + paths.add(i, replacement); + paths.remove(i + 1); + this.paths = paths; + } + return this; + } + + private List asList(String value) { + List values = new ArrayList<>(); + values.add(value); + return values; + } + + /** + * Removes the query parameter with the specified name + * + * @param name of the parameter to be removed + * @return {@link ServicePath} with the query parameter with the specified name being removed + */ + public ServicePath removeParameter(String name) { + Parameter parameter = getParameter(name); + if (nonNull(parameter)) { + this.queryParameters.remove(parameter); + } + return this; + } + + /** @return the string representing the whole fragment part of a token */ + public String fragment() { + return String.join("/", fragments()); + } + + /** + * @return true if all of token (path part, query part, fragments part) are empty, + * otherwise return false. + */ + public boolean isEmpty() { + return paths.isEmpty() && queryParameters.isEmpty() && fragments.isEmpty(); + } + + /** @return the full string representation of a {@link ServicePath} */ + public String value() { + String path = path(); + String separator = + (getRootPath().isEmpty() + || getRootPath().endsWith("/") + || path.startsWith("/") + || path.isEmpty()) + ? "" + : "/"; + return getRootPath() + separator + noRootValue(); + } + + /** {@inheritDoc} */ + public String noRootValue() { + return path() + appendQuery(query()) + appendFragment(); + } + + private String appendFragment() { + return isEmpty(fragment()) ? "" : "#" + fragment(); + } + + private String appendQuery(String query) { + return isEmpty(query) ? "" : "?" + query; + } + + private List asPathsList(String token) { + if (isNull(token) || isEmpty(token) || token.startsWith("?") || token.startsWith("#")) + return new ArrayList<>(); + return Arrays.stream(splittedPaths(token)) + .filter(p -> !p.isEmpty()) + .collect(Collectors.toCollection(LinkedList::new)); + } + + private String[] splittedPaths(String pathString) { + return parsePathPart(pathString).split("/"); + } + + private String parsePathPart(String pathString) { + return pathString.replace("!", "").split(QUERY_REGEX)[0].split(FRAGMENT_REGEX)[0]; + } + + private boolean isEmpty(String path) { + return isNull(path) || path.isEmpty(); + } + + private boolean isValidSize(List paths, List targets) { + return !targets.isEmpty() && targets.size() <= paths.size(); + } + + private List asQueryParameters(String token) { + + String queryString = queryPart(token); + if (isNull(queryString) || queryString.trim().isEmpty()) { + return new LinkedList<>(); + } + return parsedParameters(queryString); + } + + private List parsedParameters(String queryString) { + + return Stream.of(queryString.split("&")).map(part -> part.split("=")) + .collect( + Collectors.groupingBy( + keyValue -> keyValue[0], + LinkedHashMap::new, + Collectors.mapping(keyValue -> keyValue[1], Collectors.toList()))) + .entrySet().stream() + .map(entry -> new Parameter(entry.getKey(), entry.getValue())) + .collect(Collectors.toCollection(LinkedList::new)); + } + + private String queryPart(String token) { + String query = ""; + if (token.contains("?") && token.indexOf("?") < token.length() - 1) { + String[] parts = token.split(QUERY_REGEX); + + if (parts.length > 1) { + if (parts[1].split(FRAGMENT_REGEX).length > 0) { + query = parts[1].split(FRAGMENT_REGEX)[0]; + } else { + return query; + } + } else { + query = parts[0].split(FRAGMENT_REGEX)[0]; + } + + if (!query.isEmpty() && !query.contains("=")) { + throw new IllegalArgumentException("Query string [" + query + "] is missing '=' operator."); + } + } + return query; + } + + public String getRootPath() { + return rootPath; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ServicePath)) return false; + ServicePath that = (ServicePath) o; + + return paths.equals(that.paths) + && fragments.equals(that.fragments) + && queryParameters.size() == that.queryParameters.size() + && queryParameters.containsAll(that.queryParameters); + } + + public int hashCode() { + return Objects.hash(paths, queryParameters, fragments); + } + + private static class Parameter { + private String key; + private List value; + + public Parameter(String key, List value) { + this.key = key; + this.value = value; + } + + private void addValues(List moreValues) { + value.addAll(moreValues); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Parameter)) return false; + Parameter parameter = (Parameter) o; + return Objects.equals(key, parameter.key) + && value.size() == parameter.value.size() + && value.containsAll(parameter.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } + + private String asQueryString() { + return value.stream().map(value -> key + "=" + value).collect(Collectors.joining("&")); + } + } +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/UrlFormatter.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/UrlFormatter.java index 9850d4b..6180e3d 100644 --- a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/UrlFormatter.java +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/UrlFormatter.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Map; -import org.dominokit.domino.history.StateHistoryToken; import org.dominokit.rest.shared.request.exception.PathParameterMissingException; /** Formats the url by adding the query parameters and normalizing path parameters */ @@ -45,7 +44,7 @@ protected String formatUrl(String targetUrl) { String postfix = asTokenString(targetUrl); String prefix = targetUrl.replace(postfix, ""); - StateHistoryToken tempToken = new StateHistoryToken(postfix); + ServicePath tempToken = new ServicePath(postfix); replaceUrlParamsWithArguments(tempToken); @@ -56,11 +55,11 @@ private boolean hasExpressions(String url) { return (url.contains("{") && url.contains("}")) || url.contains(":"); } - private void replaceUrlParamsWithArguments(StateHistoryToken tempToken) { + private void replaceUrlParamsWithArguments(ServicePath tempToken) { replacePaths(tempToken); } - private void replacePaths(StateHistoryToken tempToken) { + private void replacePaths(ServicePath tempToken) { new ArrayList<>(tempToken.paths()) .stream() .filter(this::isExpressionToken) diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Consumes.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Consumes.java new file mode 100644 index 0000000..34a0634 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Consumes.java @@ -0,0 +1,20 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +public interface Consumes { + Produces accepts(String consumes); +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/GenericRequest.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/GenericRequest.java new file mode 100644 index 0000000..6043de9 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/GenericRequest.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +import org.dominokit.rest.shared.request.RequestMeta; +import org.dominokit.rest.shared.request.ServerRequest; + +class GenericRequest extends ServerRequest { + + public GenericRequest(RequestMeta requestMeta, R requestBean) { + super(requestMeta, requestBean); + } +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasMethod.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasMethod.java new file mode 100644 index 0000000..e674be7 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasMethod.java @@ -0,0 +1,21 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +public interface HasMethod { + + HasPath withMethod(String method); +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasPath.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasPath.java new file mode 100644 index 0000000..2721c13 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/HasPath.java @@ -0,0 +1,20 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +public interface HasPath { + Consumes withPath(String consumes); +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Produces.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Produces.java new file mode 100644 index 0000000..7cdbe61 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/Produces.java @@ -0,0 +1,20 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +public interface Produces { + RestRequestBuilder produces(String produces); +} diff --git a/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/RestRequestBuilder.java b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/RestRequestBuilder.java new file mode 100644 index 0000000..4c803d5 --- /dev/null +++ b/domino-rest-shared/src/main/java/org/dominokit/rest/shared/request/builder/RestRequestBuilder.java @@ -0,0 +1,107 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.shared.request.builder; + +import static java.util.Objects.isNull; + +import java.util.Optional; +import org.dominokit.rest.shared.request.RequestMeta; +import org.dominokit.rest.shared.request.RequestWriter; +import org.dominokit.rest.shared.request.ResponseReader; +import org.dominokit.rest.shared.request.ServerRequest; + +public class RestRequestBuilder + implements HasMethod, HasPath, Consumes, Produces { + + private String key; + private Class requestClass; + private Class responseClass; + private String method; + private String consumes; + private String produce; + private String path; + private String serviceRoot = ""; + private ResponseReader responseReader = response -> null; + private RequestWriter requestWriter = request -> null; + + public static HasMethod of( + Class requestClass, Class responseClass, String key) { + RestRequestBuilder builder = new RestRequestBuilder<>(); + builder.key = key; + builder.requestClass = requestClass; + builder.responseClass = responseClass; + return builder; + } + + @Override + public HasPath withMethod(String method) { + this.method = method; + return this; + } + + @Override + public Consumes withPath(String path) { + this.path = path; + return this; + } + + @Override + public Produces accepts(String consumes) { + this.consumes = consumes; + return this; + } + + @Override + public RestRequestBuilder produces(String produces) { + this.produce = produces; + return this; + } + + public RestRequestBuilder withServiceRoot(String serviceRoot) { + this.serviceRoot = serviceRoot; + return this; + } + + public RestRequestBuilder withResponseReader(ResponseReader responseReader) { + this.responseReader = responseReader; + return this; + } + + public RestRequestBuilder withRequestWriter(RequestWriter requestWriter) { + this.requestWriter = requestWriter; + return this; + } + + public ServerRequest build() { + return build(null); + } + + public ServerRequest build(R requestBean) { + ServerRequest request = + new GenericRequest<>( + new RequestMeta(RestRequestBuilder.class, key, requestClass, responseClass), + requestBean); + request.setHttpMethod(this.method); + request.setAccept(new String[] {this.produce}); + request.setContentType(new String[] {this.consumes}); + request.setPath(this.path); + request.setServiceRoot(isNull(this.serviceRoot) ? "" : this.serviceRoot); + Optional.ofNullable(this.responseReader).ifPresent(request::setResponseReader); + Optional.ofNullable(this.requestWriter).ifPresent(request::setRequestWriter); + + return request; + } +} diff --git a/domino-rest-test/pom.xml b/domino-rest-test/pom.xml index c08cdf0..63d537e 100644 --- a/domino-rest-test/pom.xml +++ b/domino-rest-test/pom.xml @@ -5,7 +5,7 @@ domino-rest org.dominokit - 2.0.0-RC1 + 2.0.0-RC2 4.0.0 diff --git a/domino-rest-test/src/test/java/org/dominokit/rest/model/JakartaResponseService.java b/domino-rest-test/src/test/java/org/dominokit/rest/model/JakartaResponseService.java new file mode 100644 index 0000000..a6f747c --- /dev/null +++ b/domino-rest-test/src/test/java/org/dominokit/rest/model/JakartaResponseService.java @@ -0,0 +1,34 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.model; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.dominokit.rest.shared.request.service.annotations.RequestFactory; + +@RequestFactory +@Path("test") +public interface JakartaResponseService { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + Response getResponse(); +} diff --git a/domino-rest-test/src/test/java/org/dominokit/rest/test/ResponseReturnTypeTest.java b/domino-rest-test/src/test/java/org/dominokit/rest/test/ResponseReturnTypeTest.java new file mode 100644 index 0000000..209a523 --- /dev/null +++ b/domino-rest-test/src/test/java/org/dominokit/rest/test/ResponseReturnTypeTest.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2019 Dominokit + * + * 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 org.dominokit.rest.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.vertx.core.Vertx; +import io.vertx.junit5.VertxExtension; +import io.vertx.junit5.VertxTestContext; +import org.dominokit.rest.model.JakartaResponseServiceFactory; +import org.dominokit.rest.shared.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(VertxExtension.class) +public class ResponseReturnTypeTest extends BaseRestTest { + + @Test + @DisplayName("Test using jakarta.ws.rs.core.Response as a return type") + void nullQueryParamAsEmpty(Vertx vertx, VertxTestContext testContext) { + JakartaResponseServiceFactory.INSTANCE + .getResponse() + .onSuccess( + response -> { + assertThat(Response.class.isAssignableFrom(response.getClass())); + testContext.completeNow(); + }) + .send(); + } +} diff --git a/pom.xml b/pom.xml index aeb0ec4..1d830c6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.dominokit domino-rest - 2.0.0-RC1 + 2.0.0-RC2 pom domino-rest @@ -70,7 +70,7 @@ HEAD-SNAPSHOT - 2.0.0-RC1 + 2.0.0-RC2 11 11 UTF-8 @@ -88,14 +88,13 @@ 3.0.0-M5 1.1.0 - 2.10.0 + 2.12.0 1.0.3 - 1.0.4 - 1.0.3 - 1.0.2 + 1.0.5 + 1.0.3 3.1.0 2.16.0 - 1.2.1 + 1.2.3