001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.web.client.match;
018
019import java.io.IOException;
020import java.net.URI;
021import java.util.List;
022import java.util.Map;
023import javax.xml.xpath.XPathExpressionException;
024
025import org.hamcrest.Matcher;
026
027import org.springframework.http.HttpMethod;
028import org.springframework.http.client.ClientHttpRequest;
029import org.springframework.test.util.AssertionErrors;
030import org.springframework.test.web.client.MockRestServiceServer;
031import org.springframework.test.web.client.RequestMatcher;
032import org.springframework.util.Assert;
033import org.springframework.util.MultiValueMap;
034import org.springframework.web.util.UriComponentsBuilder;
035
036import static org.hamcrest.MatcherAssert.assertThat;
037import static org.springframework.test.util.AssertionErrors.assertEquals;
038import static org.springframework.test.util.AssertionErrors.assertTrue;
039
040/**
041 * Static factory methods for {@link RequestMatcher} classes. Typically used to
042 * provide input for {@link MockRestServiceServer#expect(RequestMatcher)}.
043 *
044 * <h3>Eclipse Users</h3>
045 * <p>Consider adding this class as a Java editor favorite. To navigate to
046 * this setting, open the Preferences and type "favorites".
047 *
048 * @author Craig Walls
049 * @author Rossen Stoyanchev
050 * @since 3.2
051 */
052public abstract class MockRestRequestMatchers {
053
054        /**
055         * Match to any request.
056         */
057        public static RequestMatcher anything() {
058                return new RequestMatcher() {
059                        @Override
060                        public void match(ClientHttpRequest request) throws AssertionError {
061                        }
062                };
063        }
064
065        /**
066         * Assert the {@link HttpMethod} of the request.
067         * @param method the HTTP method
068         * @return the request matcher
069         */
070        public static RequestMatcher method(final HttpMethod method) {
071                Assert.notNull(method, "'method' must not be null");
072                return new RequestMatcher() {
073                        @Override
074                        public void match(ClientHttpRequest request) throws AssertionError {
075                                AssertionErrors.assertEquals("Unexpected HttpMethod", method, request.getMethod());
076                        }
077                };
078        }
079
080        /**
081         * Assert the request URI string with the given matcher.
082         * @param matcher String matcher for the expected URI
083         * @return the request matcher
084         */
085        public static RequestMatcher requestTo(final Matcher<String> matcher) {
086                Assert.notNull(matcher, "'matcher' must not be null");
087                return new RequestMatcher() {
088                        @Override
089                        public void match(ClientHttpRequest request) throws IOException, AssertionError {
090                                assertThat("Request URI", request.getURI().toString(), matcher);
091                        }
092                };
093        }
094
095        /**
096         * Assert the request URI string.
097         * @param expectedUri the expected URI
098         * @return the request matcher
099         */
100        public static RequestMatcher requestTo(final String expectedUri) {
101                Assert.notNull(expectedUri, "'uri' must not be null");
102                return new RequestMatcher() {
103                        @Override
104                        public void match(ClientHttpRequest request) throws IOException, AssertionError {
105                                assertEquals("Request URI", expectedUri, request.getURI().toString());
106                        }
107                };
108        }
109
110        /**
111         * Expect a request to the given URI.
112         * @param uri the expected URI
113         * @return the request matcher
114         */
115        public static RequestMatcher requestTo(final URI uri) {
116                Assert.notNull(uri, "'uri' must not be null");
117                return new RequestMatcher() {
118                        @Override
119                        public void match(ClientHttpRequest request) throws IOException, AssertionError {
120                                AssertionErrors.assertEquals("Unexpected request", uri, request.getURI());
121                        }
122                };
123        }
124
125        /**
126         * Assert request query parameter values with the given Hamcrest matcher.
127         */
128        @SafeVarargs
129        public static RequestMatcher queryParam(final String name, final Matcher<? super String>... matchers) {
130                return new RequestMatcher() {
131                        @Override
132                        public void match(ClientHttpRequest request) {
133                                MultiValueMap<String, String> params = getQueryParams(request);
134                                assertValueCount("query param", name, params, matchers.length);
135                                for (int i = 0 ; i < matchers.length; i++) {
136                                        assertThat("Query param", params.get(name).get(i), matchers[i]);
137                                }
138                        }
139                };
140        }
141
142        /**
143         * Assert request query parameter values.
144         */
145        public static RequestMatcher queryParam(final String name, final String... expectedValues) {
146                return new RequestMatcher() {
147                        @Override
148                        public void match(ClientHttpRequest request) {
149                                MultiValueMap<String, String> params = getQueryParams(request);
150                                assertValueCount("query param", name, params, expectedValues.length);
151                                for (int i = 0 ; i < expectedValues.length; i++) {
152                                        assertEquals("Query param + [" + name + "]", expectedValues[i], params.get(name).get(i));
153                                }
154                        }
155                };
156        }
157
158        private static MultiValueMap<String, String> getQueryParams(ClientHttpRequest request) {
159                return UriComponentsBuilder.fromUri(request.getURI()).build().getQueryParams();
160        }
161
162        private static void assertValueCount(String valueType, final String name,
163                        MultiValueMap<String, String> map, int count) {
164
165                List<String> values = map.get(name);
166
167                String message = "Expected " + valueType + " <" + name + ">";
168                assertTrue(message + " to exist but was null", values != null);
169
170                assertTrue(message + " to have at least <" + count + "> values but found " + values,
171                                count <= values.size());
172        }
173
174        /**
175         * Assert request header values with the given Hamcrest matcher.
176         */
177        @SafeVarargs
178        public static RequestMatcher header(final String name, final Matcher<? super String>... matchers) {
179                return new RequestMatcher() {
180                        @Override
181                        public void match(ClientHttpRequest request) {
182                                assertValueCount("header", name, request.getHeaders(), matchers.length);
183                                for (int i = 0 ; i < matchers.length; i++) {
184                                        assertThat("Request header", request.getHeaders().get(name).get(i), matchers[i]);
185                                }
186                        }
187                };
188        }
189
190        /**
191         * Assert request header values.
192         */
193        public static RequestMatcher header(final String name, final String... expectedValues) {
194                return new RequestMatcher() {
195                        @Override
196                        public void match(ClientHttpRequest request) {
197                                assertValueCount("header", name, request.getHeaders(), expectedValues.length);
198                                for (int i = 0 ; i < expectedValues.length; i++) {
199                                        assertEquals("Request header + [" + name + "]",
200                                                        expectedValues[i], request.getHeaders().get(name).get(i));
201                                }
202                        }
203                };
204        }
205
206        /**
207         * Access to request body matchers.
208         */
209        public static ContentRequestMatchers content() {
210                return new ContentRequestMatchers();
211        }
212
213        /**
214         * Access to request body matchers using a
215         * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression to
216         * inspect a specific subset of the body. The JSON path expression can be a
217         * parameterized string using formatting specifiers as defined in
218         * {@link String#format(String, Object...)}.
219         * @param expression the JSON path optionally parameterized with arguments
220         * @param args arguments to parameterize the JSON path expression with
221         */
222        public static JsonPathRequestMatchers jsonPath(String expression, Object... args) {
223                return new JsonPathRequestMatchers(expression, args);
224        }
225
226        /**
227         * Access to request body matchers using a
228         * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression to
229         * inspect a specific subset of the body and a Hamcrest match for asserting
230         * the value found at the JSON path.
231         * @param expression the JSON path expression
232         * @param matcher a matcher for the value expected at the JSON path
233         */
234        public static <T> RequestMatcher jsonPath(String expression, Matcher<T> matcher) {
235                return new JsonPathRequestMatchers(expression).value(matcher);
236        }
237
238        /**
239         * Access to request body matchers using an XPath to inspect a specific
240         * subset of the body. The XPath expression can be a parameterized string
241         * using formatting specifiers as defined in
242         * {@link String#format(String, Object...)}.
243         * @param expression the XPath optionally parameterized with arguments
244         * @param args arguments to parameterize the XPath expression with
245         */
246        public static XpathRequestMatchers xpath(String expression, Object... args) throws XPathExpressionException {
247                return new XpathRequestMatchers(expression, null, args);
248        }
249
250        /**
251         * Access to response body matchers using an XPath to inspect a specific
252         * subset of the body. The XPath expression can be a parameterized string
253         * using formatting specifiers as defined in
254         * {@link String#format(String, Object...)}.
255         * @param expression the XPath optionally parameterized with arguments
256         * @param namespaces namespaces referenced in the XPath expression
257         * @param args arguments to parameterize the XPath expression with
258         */
259        public static XpathRequestMatchers xpath(String expression, Map<String, String> namespaces, Object... args)
260                        throws XPathExpressionException {
261
262                return new XpathRequestMatchers(expression, namespaces, args);
263        }
264
265}