Add support for configuring default request and response preprocessors

See gh-424
This commit is contained in:
Filip Hrisafov
2017-09-02 10:35:11 +02:00
committed by Andy Wilkinson
parent 22cf08a9ad
commit 4f8b173836
20 changed files with 699 additions and 52 deletions

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2014-2017 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
*
* 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.springframework.restdocs.config;
import java.util.Map;
import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.restdocs.generate.RestDocumentationGenerator;
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
import org.springframework.restdocs.operation.preprocess.Preprocessors;
/**
* A configurer that can be used to configure the default operation preprocessors that need to be used.
*
* @param <PARENT> The type of the configurer's parent
* @param <TYPE> The concrete type of the configurer to be returned from chained methods
* @author Filip Hrisafov
* @since 2.0.0
*/
public abstract class OperationPreprocessorsConfigurer<PARENT, TYPE>
extends AbstractNestedConfigurer<PARENT> {
private OperationRequestPreprocessor defaultOperationRequestPreprocessor;
private OperationResponsePreprocessor defaultOperationResponsePreprocessor;
/**
* Creates a new {@code OperationPreprocessorConfigurer} with the given {@code parent}.
*
* @param parent the parent
*/
protected OperationPreprocessorsConfigurer(PARENT parent) {
super(parent);
}
@Override
public void apply(Map<String, Object> configuration, RestDocumentationContext context) {
configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR,
this.defaultOperationRequestPreprocessor);
configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR,
this.defaultOperationResponsePreprocessor);
}
/**
* Configures the default documentation operation request preprocessors.
*
* @param preprocessors the preprocessors
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public TYPE withDefaultRequestPreprocessors(OperationPreprocessor... preprocessors) {
this.defaultOperationRequestPreprocessor = Preprocessors.preprocessRequest(preprocessors);
return (TYPE) this;
}
/**
* Configures the default documentation operation response preprocessors.
*
* @param preprocessors the preprocessors
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public TYPE withDefaultResponsePreprocessors(OperationPreprocessor... preprocessors) {
this.defaultOperationResponsePreprocessor = Preprocessors.preprocessResponse(preprocessors);
return (TYPE) this;
}
}

View File

@@ -36,12 +36,14 @@ import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
* Abstract base class for the configuration of Spring REST Docs.
*
* @param <S> The concrete type of the {@link SnippetConfigurer}.
* @param <P> The concrete type of the {@link OperationPreprocessorsConfigurer}
* @param <T> The concrete type of this configurer, to be returned from methods that
* support chaining
* @author Andy Wilkinson
* @author Filip Hrisafov
* @since 1.1.0
*/
public abstract class RestDocumentationConfigurer<S extends AbstractConfigurer, T> {
public abstract class RestDocumentationConfigurer<S extends AbstractConfigurer, P extends AbstractConfigurer, T> {
private final WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer();
@@ -55,6 +57,14 @@ public abstract class RestDocumentationConfigurer<S extends AbstractConfigurer,
*/
public abstract S snippets();
/**
* Returns an {@link OperationPreprocessorsConfigurer} that can be used to configure the operation request and
* response preprocessors that will be used during the documentation.
*
* @return the operation preprocessors configurer
*/
public abstract P operationPreprocessors();
/**
* Configures the {@link TemplateEngine} that will be used for snippet rendering.
*
@@ -90,6 +100,7 @@ public abstract class RestDocumentationConfigurer<S extends AbstractConfigurer,
protected final void apply(Map<String, Object> configuration,
RestDocumentationContext context) {
List<AbstractConfigurer> configurers = Arrays.asList(snippets(),
operationPreprocessors(),
this.templateEngineConfigurer, this.writerResolverConfigurer);
for (AbstractConfigurer configurer : configurers) {
configurer.apply(configuration, context);

View File

@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
* @param <REQ> the request type that can be handled
* @param <RESP> the response type that can be handled
* @author Andy Wilkinson
* @author Filip Hrisafov
* @since 1.1.0
*/
public final class RestDocumentationGenerator<REQ, RESP> {
@@ -55,6 +56,19 @@ public final class RestDocumentationGenerator<REQ, RESP> {
*/
public static final String ATTRIBUTE_NAME_DEFAULT_SNIPPETS = "org.springframework.restdocs.defaultSnippets";
/**
* Name of the operation attribute used to hold the default operation request preprocessor.
*/
public static final String ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR =
"org.springframework.restdocs.defaultOperationRequestPreprocessor";
/**
* Name of the operation attribute used to hold the default operation response preprocessor.
*/
public static final String ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR =
"org.springframework.restdocs.defaultOperationResponsePreprocessor";
private final String identifier;
private final OperationRequestPreprocessor requestPreprocessor;
@@ -183,12 +197,9 @@ public final class RestDocumentationGenerator<REQ, RESP> {
* @throws RestDocumentationGenerationException if a failure occurs during handling
*/
public void handle(REQ request, RESP response, Map<String, Object> configuration) {
OperationRequest operationRequest = this.requestPreprocessor
.preprocess(this.requestConverter.convert(request));
OperationResponse operationResponse = this.responsePreprocessor
.preprocess(this.responseConverter.convert(response));
Map<String, Object> attributes = new HashMap<>(configuration);
OperationRequest operationRequest = preprocessRequest(request, attributes);
OperationResponse operationResponse = preprocessResponse(response, attributes);
Operation operation = new StandardOperation(this.identifier, operationRequest,
operationResponse, attributes);
try {
@@ -228,6 +239,46 @@ public final class RestDocumentationGenerator<REQ, RESP> {
return combinedSnippets;
}
private OperationRequest preprocessRequest(REQ request, Map<String, Object> configuration) {
List<OperationRequestPreprocessor> requestPreprocessors = getRequestPreprocessors(configuration);
OperationRequest operationRequest = this.requestConverter.convert(request);
for (OperationRequestPreprocessor preprocessor : requestPreprocessors) {
operationRequest = preprocessor.preprocess(operationRequest);
}
return operationRequest;
}
private List<OperationRequestPreprocessor> getRequestPreprocessors(Map<String, Object> configuration) {
List<OperationRequestPreprocessor> preprocessors = new ArrayList<>(2);
preprocessors.add(this.requestPreprocessor);
OperationRequestPreprocessor defaultRequestPreprocessor = (OperationRequestPreprocessor) configuration.get(
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_REQUEST_PREPROCESSOR);
if (defaultRequestPreprocessor != null) {
preprocessors.add(defaultRequestPreprocessor);
}
return preprocessors;
}
private OperationResponse preprocessResponse(RESP response, Map<String, Object> configuration) {
List<OperationResponsePreprocessor> responsePreprocessors = getResponsePreprocessors(configuration);
OperationResponse operationResponse = this.responseConverter.convert(response);
for (OperationResponsePreprocessor preprocessor : responsePreprocessors) {
operationResponse = preprocessor.preprocess(operationResponse);
}
return operationResponse;
}
private List<OperationResponsePreprocessor> getResponsePreprocessors(Map<String, Object> configuration) {
List<OperationResponsePreprocessor> preprocessors = new ArrayList<>(2);
preprocessors.add(this.responsePreprocessor);
OperationResponsePreprocessor defaultResponsePreprocessor = (OperationResponsePreprocessor) configuration.get(
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_OPERATION_RESPONSE_PREPROCESSOR);
if (defaultResponsePreprocessor != null) {
preprocessors.add(defaultResponsePreprocessor);
}
return preprocessors;
}
private static final class IdentityOperationRequestPreprocessor
implements OperationRequestPreprocessor {