From 8f8fbe45af01eda2928212a8e71e8ca4277f68e7 Mon Sep 17 00:00:00 2001 From: Augusto Ravazoli Date: Sat, 29 Apr 2023 14:11:11 -0300 Subject: [PATCH] Add builders as alternatives to factories when creating operation requests and responses The intent of this change is to provide a better api to work with, dispensing repeated code from the factories Fixes gh-886 --- .../operation/OperationRequestBuilder.java | 152 ++++++++++++++++++ .../operation/OperationResponseBuilder.java | 114 +++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestBuilder.java create mode 100644 spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseBuilder.java diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestBuilder.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestBuilder.java new file mode 100644 index 00000000..e97f48b5 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationRequestBuilder.java @@ -0,0 +1,152 @@ +/* + * Copyright 2014-2022 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 org.springframework.restdocs.operation; + +import java.net.URI; +import java.util.Collection; +import java.util.Collections; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +/** + * A builder for creating {@link OperationRequest OperationRequests}. + * + * @author Augusto Ravazoli + */ +public class OperationRequestBuilder { + + private URI uri; + private HttpMethod method; + private byte[] content; + private HttpHeaders headers; + private Collection parts; + private Collection cookies; + + /** + * Creates a new OperationRequestBuilder. + * @return the {@code OperationRequestBuilder}. + */ + public OperationRequestBuilder() {} + + /** + * Creates a new OperationRequestBuilder using an existing {@link OperationRequest} as default. + * @param The original {@link OperationRequest}. + * @return {@code OperationRequestBuilder}. + */ + public OperationRequestBuilder(OperationRequest original) { + uri = original.getUri(); + method = original.getMethod(); + content = original.getContent(); + headers = original.getHeaders(); + parts = original.getParts(); + cookies = original.getCookies(); + } + + /** + * Sets the URI of the request. + * @param uri the request's uri. + * @return a reference to this object. + */ + public OperationRequestBuilder uri(URI uri) { + this.uri = uri; + return this; + } + + /** + * Sets the HTTP method of the request. + * @param method the request's method. + * @return a reference to this object. + */ + public OperationRequestBuilder method(HttpMethod method) { + this.method = method; + return this; + } + + /** + * Sets the content of the request. + * @param content the request's content. + * @return a reference to this object. + */ + public OperationRequestBuilder content(byte[] content) { + this.content = content; + return this; + } + + /** + * Sets the headers of the request. + * @param headers the request's headers. + * @return a reference to this object. + */ + public OperationRequestBuilder headers(HttpHeaders headers) { + this.headers = headers; + return this; + } + + /** + * Sets the parts of the request. + * @param parts the request's parts. + * @return a reference to this object. + */ + public OperationRequestBuilder parts(Collection parts) { + this.parts = parts; + return this; + } + + /** + * Sets the cookies of the request. + * @param cookies the request's cookies. + * @return a reference to this object. + */ + public OperationRequestBuilder cookies(Collection cookies) { + this.cookies = cookies; + return this; + } + + /** + * Builds the new operation request object. The {@code headers} will be augmented + * to ensure that they always include a {@code Content-Length} header if the request + * has any content and a {@code Host} header. + * @return the {@code OperationRequest}. + */ + public OperationRequest build() { + return new StandardOperationRequest( + uri, + method, + content, + augmentHeaders(headers, uri, content), + parts != null ? parts : Collections.emptyList(), + cookies != null ? cookies : Collections.emptyList() + ); + } + + private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, URI uri, byte[] content) { + String hostHeader = createHostHeader(uri); + return new HttpHeadersHelper(originalHeaders) + .addIfAbsent(HttpHeaders.HOST, hostHeader) + .setContentLengthHeader(content) + .getHeaders(); + } + + private String createHostHeader(URI uri) { + if (uri.getPort() == -1) { + return uri.getHost(); + } + return uri.getHost() + ":" + uri.getPort(); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseBuilder.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseBuilder.java new file mode 100644 index 00000000..c2be0743 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/operation/OperationResponseBuilder.java @@ -0,0 +1,114 @@ +/* + * Copyright 2014-2022 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 org.springframework.restdocs.operation; + +import java.util.Collection; +import java.util.Collections; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; + +/** + * A builder for creating {@code OperationResponse OperationsResponses}. + * + * @author Augusto Ravazoli + */ +public class OperationResponseBuilder { + + private HttpStatusCode status; + private HttpHeaders headers; + private byte[] content; + private Collection cookies; + + /** + * Creates a new OperationResponseBuilder. + * @return the {@code OperationResponseBuilder}. + */ + public OperationResponseBuilder() {} + + /** + * Creates a new OperationResponseBuilder using an existing {@link OperationResponse} as default. + * @param The original {@link OperationResponse}. + * @return {@code OperationResponseBuilder}. + */ + public OperationResponseBuilder(OperationResponse original) { + status = original.getStatus(); + headers = original.getHeaders(); + content = original.getContent(); + cookies = original.getCookies(); + } + + /** + * Sets the status code of the response. + * @param status the response's status. + * @return a reference to this object. + */ + public OperationResponseBuilder status(HttpStatusCode status) { + this.status = status; + return this; + } + + /** + * Sets the headers of the response. + * @param headers the response's headers. + * @return a reference to this object. + */ + public OperationResponseBuilder headers(HttpHeaders headers) { + this.headers = headers; + return this; + } + + /** + * Sets the content of the response. + * @param content the request's content. + * @return a reference to this object. + */ + public OperationResponseBuilder content(byte[] content) { + this.content = content; + return this; + } + + /** + * Sets the cookies of the response. + * @param cookies the response's cookies. + * @return a reference to this object. + */ + public OperationResponseBuilder cookies(Collection cookies) { + this.cookies = cookies; + return this; + } + + /** + * Builds the new operation response object. If the response has any content, the given + * {@code headers} will be augmented to ensure that they include a + * {@code Content-Length} header. + * @return the {@code OperationResponse}. + */ + public OperationResponse build() { + return new StandardOperationResponse( + status, + augmentHeaders(headers, content), + content, + cookies != null ? cookies : Collections.emptyList() + ); + } + + private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, byte[] content) { + return new HttpHeadersHelper(originalHeaders).setContentLengthHeader(content).getHeaders(); + } + +}