Add support for delete operations
This commit adds a `@DeleteOperation` annotation that can be used to indicate that an endpoint's operation is meant to delete a resource. Such operation is mapped to a DELETE http method. Closes gh-10023
This commit is contained in:
		
							parent
							
								
									26b93e9454
								
							
						
					
					
						commit
						4c7088981f
					
				| 
						 | 
				
			
			@ -217,9 +217,15 @@ public abstract class AnnotationEndpointDiscoverer<T extends Operation, K>
 | 
			
		|||
 | 
			
		||||
	private T createOperationIfPossible(String endpointId, String beanName,
 | 
			
		||||
			Method method) {
 | 
			
		||||
		T readOperation = createReadOperationIfPossible(endpointId, beanName, method);
 | 
			
		||||
		return (readOperation != null ? readOperation
 | 
			
		||||
				: createWriteOperationIfPossible(endpointId, beanName, method));
 | 
			
		||||
		T operation = createReadOperationIfPossible(endpointId, beanName, method);
 | 
			
		||||
		if (operation != null) {
 | 
			
		||||
			return operation;
 | 
			
		||||
		}
 | 
			
		||||
		operation = createWriteOperationIfPossible(endpointId, beanName, method);
 | 
			
		||||
		if (operation != null) {
 | 
			
		||||
			return operation;
 | 
			
		||||
		}
 | 
			
		||||
		return createDeleteOperationIfPossible(endpointId, beanName, method);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private T createReadOperationIfPossible(String endpointId, String beanName,
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +240,12 @@ public abstract class AnnotationEndpointDiscoverer<T extends Operation, K>
 | 
			
		|||
				WriteOperation.class, OperationType.WRITE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private T createDeleteOperationIfPossible(String endpointId, String beanName,
 | 
			
		||||
			Method method) {
 | 
			
		||||
		return createOperationIfPossible(endpointId, beanName, method,
 | 
			
		||||
				DeleteOperation.class, OperationType.DELETE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private T createOperationIfPossible(String endpointId, String beanName, Method method,
 | 
			
		||||
			Class<? extends Annotation> operationAnnotation,
 | 
			
		||||
			OperationType operationType) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-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.boot.endpoint;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.Documented;
 | 
			
		||||
import java.lang.annotation.ElementType;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Identifies a method on an {@link Endpoint} as being a delete operation.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @since 2.0.0
 | 
			
		||||
 */
 | 
			
		||||
@Target(ElementType.METHOD)
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Documented
 | 
			
		||||
public @interface DeleteOperation {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,11 @@ public enum OperationType {
 | 
			
		|||
	/**
 | 
			
		||||
	 * A write operation.
 | 
			
		||||
	 */
 | 
			
		||||
	WRITE
 | 
			
		||||
	WRITE,
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * A delete operation.
 | 
			
		||||
	 */
 | 
			
		||||
	DELETE
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ class EndpointMBeanInfoAssembler {
 | 
			
		|||
		if (type == OperationType.READ) {
 | 
			
		||||
			return MBeanOperationInfo.INFO;
 | 
			
		||||
		}
 | 
			
		||||
		if (type == OperationType.WRITE) {
 | 
			
		||||
		if (type == OperationType.WRITE || type == OperationType.DELETE) {
 | 
			
		||||
			return MBeanOperationInfo.ACTION;
 | 
			
		||||
		}
 | 
			
		||||
		return MBeanOperationInfo.UNKNOWN;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -205,6 +205,9 @@ public class WebAnnotationEndpointDiscoverer extends
 | 
			
		|||
			if (operationType == OperationType.WRITE) {
 | 
			
		||||
				return WebEndpointHttpMethod.POST;
 | 
			
		||||
			}
 | 
			
		||||
			if (operationType == OperationType.DELETE) {
 | 
			
		||||
				return WebEndpointHttpMethod.DELETE;
 | 
			
		||||
			}
 | 
			
		||||
			return WebEndpointHttpMethod.GET;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,11 @@ public enum WebEndpointHttpMethod {
 | 
			
		|||
	/**
 | 
			
		||||
	 * An HTTP POST request.
 | 
			
		||||
	 */
 | 
			
		||||
	POST
 | 
			
		||||
	POST,
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * An HTTP DELETE request.
 | 
			
		||||
	 */
 | 
			
		||||
	DELETE
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,12 +64,14 @@ public class AnnotationEndpointDiscovererTests {
 | 
			
		|||
			assertThat(endpoints).containsOnlyKeys("test");
 | 
			
		||||
			Map<Method, TestEndpointOperation> operations = mapOperations(
 | 
			
		||||
					endpoints.get("test"));
 | 
			
		||||
			assertThat(operations).hasSize(3);
 | 
			
		||||
			assertThat(operations).hasSize(4);
 | 
			
		||||
			assertThat(operations).containsKeys(
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "getAll"),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "getOne",
 | 
			
		||||
							String.class),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "update", String.class,
 | 
			
		||||
							String.class),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "deleteOne",
 | 
			
		||||
							String.class));
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -82,13 +84,15 @@ public class AnnotationEndpointDiscovererTests {
 | 
			
		|||
			assertThat(endpoints).containsOnlyKeys("test");
 | 
			
		||||
			Map<Method, TestEndpointOperation> operations = mapOperations(
 | 
			
		||||
					endpoints.get("test"));
 | 
			
		||||
			assertThat(operations).hasSize(4);
 | 
			
		||||
			assertThat(operations).hasSize(5);
 | 
			
		||||
			assertThat(operations).containsKeys(
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "getAll"),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "getOne",
 | 
			
		||||
							String.class),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "update", String.class,
 | 
			
		||||
							String.class),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpoint.class, "deleteOne",
 | 
			
		||||
							String.class),
 | 
			
		||||
					ReflectionUtils.findMethod(TestEndpointSubclass.class,
 | 
			
		||||
							"updateWithMoreArguments", String.class, String.class,
 | 
			
		||||
							String.class));
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +118,7 @@ public class AnnotationEndpointDiscovererTests {
 | 
			
		|||
			assertThat(endpoints).containsOnlyKeys("test");
 | 
			
		||||
			Map<Method, TestEndpointOperation> operations = mapOperations(
 | 
			
		||||
					endpoints.get("test"));
 | 
			
		||||
			assertThat(operations).hasSize(3);
 | 
			
		||||
			assertThat(operations).hasSize(4);
 | 
			
		||||
			operations.values()
 | 
			
		||||
					.forEach(operation -> assertThat(operation.getInvoker())
 | 
			
		||||
							.isNotInstanceOf(CachingOperationInvoker.class));
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +137,7 @@ public class AnnotationEndpointDiscovererTests {
 | 
			
		|||
			assertThat(endpoints).containsOnlyKeys("test");
 | 
			
		||||
			Map<Method, TestEndpointOperation> operations = mapOperations(
 | 
			
		||||
					endpoints.get("test"));
 | 
			
		||||
			assertThat(operations).hasSize(3);
 | 
			
		||||
			assertThat(operations).hasSize(4);
 | 
			
		||||
			operations.values()
 | 
			
		||||
					.forEach(operation -> assertThat(operation.getInvoker())
 | 
			
		||||
							.isNotInstanceOf(CachingOperationInvoker.class));
 | 
			
		||||
| 
						 | 
				
			
			@ -232,6 +236,11 @@ public class AnnotationEndpointDiscovererTests {
 | 
			
		|||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public void deleteOne(@Selector String id) {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void someOtherMethod() {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ import reactor.core.publisher.Mono;
 | 
			
		|||
 | 
			
		||||
import org.springframework.boot.endpoint.CachingConfiguration;
 | 
			
		||||
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
 | 
			
		||||
import org.springframework.boot.endpoint.DeleteOperation;
 | 
			
		||||
import org.springframework.boot.endpoint.Endpoint;
 | 
			
		||||
import org.springframework.boot.endpoint.ReadOperation;
 | 
			
		||||
import org.springframework.boot.endpoint.WriteOperation;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +110,15 @@ public class EndpointMBeanTests {
 | 
			
		|||
						new Object[] { "one" }, new String[] { String.class.getName() });
 | 
			
		||||
				assertThat(updatedOneResponse).isEqualTo("1");
 | 
			
		||||
 | 
			
		||||
				// deleteOne
 | 
			
		||||
				Object deleteResponse = this.server.invoke(objectName, "deleteOne",
 | 
			
		||||
						new Object[] { "one" }, new String[] { String.class.getName() });
 | 
			
		||||
				assertThat(oneResponse).isEqualTo("ONE");
 | 
			
		||||
 | 
			
		||||
				// getOne validation after delete
 | 
			
		||||
				updatedOneResponse = this.server.invoke(objectName, "getOne",
 | 
			
		||||
						new Object[] { "one" }, new String[] { String.class.getName() });
 | 
			
		||||
				assertThat(updatedOneResponse).isNull();
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex) {
 | 
			
		||||
				throw new AssertionError("Failed to invoke method on FooEndpoint", ex);
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +133,8 @@ public class EndpointMBeanTests {
 | 
			
		|||
			try {
 | 
			
		||||
				MBeanInfo mBeanInfo = this.server.getMBeanInfo(objectName);
 | 
			
		||||
				Map<String, MBeanOperationInfo> operations = mapOperations(mBeanInfo);
 | 
			
		||||
				assertThat(operations).containsOnlyKeys("getAll", "getOne", "update");
 | 
			
		||||
				assertThat(operations).containsOnlyKeys("getAll", "getOne", "update",
 | 
			
		||||
						"deleteOne");
 | 
			
		||||
				assertOperation(operations.get("getAll"), String.class,
 | 
			
		||||
						MBeanOperationInfo.INFO, new Class<?>[0]);
 | 
			
		||||
				assertOperation(operations.get("getOne"), String.class,
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +142,8 @@ public class EndpointMBeanTests {
 | 
			
		|||
				assertOperation(operations.get("update"), Void.TYPE,
 | 
			
		||||
						MBeanOperationInfo.ACTION,
 | 
			
		||||
						new Class<?>[] { String.class, String.class });
 | 
			
		||||
				assertOperation(operations.get("deleteOne"), Void.TYPE,
 | 
			
		||||
						MBeanOperationInfo.ACTION, new Class<?>[] { String.class });
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex) {
 | 
			
		||||
				throw new AssertionError("Failed to retrieve MBeanInfo of FooEndpoint",
 | 
			
		||||
| 
						 | 
				
			
			@ -305,6 +318,11 @@ public class EndpointMBeanTests {
 | 
			
		|||
			this.all.put(name, new Foo(value));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public void deleteOne(FooName name) {
 | 
			
		||||
			this.all.remove(name);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "reactive")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import org.junit.rules.ExpectedException;
 | 
			
		|||
import org.springframework.boot.endpoint.CachingConfiguration;
 | 
			
		||||
import org.springframework.boot.endpoint.CachingOperationInvoker;
 | 
			
		||||
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
 | 
			
		||||
import org.springframework.boot.endpoint.DeleteOperation;
 | 
			
		||||
import org.springframework.boot.endpoint.Endpoint;
 | 
			
		||||
import org.springframework.boot.endpoint.EndpointExposure;
 | 
			
		||||
import org.springframework.boot.endpoint.EndpointInfo;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +72,7 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
			Map<String, JmxEndpointOperation> operationByName = mapOperations(
 | 
			
		||||
					endpoints.get("test").getOperations());
 | 
			
		||||
			assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
 | 
			
		||||
					"update");
 | 
			
		||||
					"update", "deleteSomething");
 | 
			
		||||
			JmxEndpointOperation getAll = operationByName.get("getAll");
 | 
			
		||||
			assertThat(getAll.getDescription())
 | 
			
		||||
					.isEqualTo("Invoke getAll for endpoint test");
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +93,12 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
			assertThat(update.getParameters()).hasSize(2);
 | 
			
		||||
			hasDefaultParameter(update, 0, String.class);
 | 
			
		||||
			hasDefaultParameter(update, 1, String.class);
 | 
			
		||||
			JmxEndpointOperation deleteSomething = operationByName.get("deleteSomething");
 | 
			
		||||
			assertThat(deleteSomething.getDescription())
 | 
			
		||||
					.isEqualTo("Invoke deleteSomething for endpoint test");
 | 
			
		||||
			assertThat(deleteSomething.getOutputType()).isEqualTo(Void.TYPE);
 | 
			
		||||
			assertThat(deleteSomething.getParameters()).hasSize(1);
 | 
			
		||||
			hasDefaultParameter(deleteSomething, 0, String.class);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +143,7 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
			Map<String, JmxEndpointOperation> operationByName = mapOperations(
 | 
			
		||||
					endpoints.get("test").getOperations());
 | 
			
		||||
			assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
 | 
			
		||||
					"update", "getAnother");
 | 
			
		||||
					"update", "deleteSomething", "getAnother");
 | 
			
		||||
			JmxEndpointOperation getAnother = operationByName.get("getAnother");
 | 
			
		||||
			assertThat(getAnother.getDescription()).isEqualTo("Get another thing");
 | 
			
		||||
			assertThat(getAnother.getOutputType()).isEqualTo(Object.class);
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +160,7 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
			Map<String, JmxEndpointOperation> operationByName = mapOperations(
 | 
			
		||||
					endpoints.get("test").getOperations());
 | 
			
		||||
			assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
 | 
			
		||||
					"update");
 | 
			
		||||
					"update", "deleteSomething");
 | 
			
		||||
			JmxEndpointOperation getAll = operationByName.get("getAll");
 | 
			
		||||
			assertThat(getAll.getInvoker()).isInstanceOf(CachingOperationInvoker.class);
 | 
			
		||||
			assertThat(((CachingOperationInvoker) getAll.getInvoker()).getTimeToLive())
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +178,7 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
					Map<String, JmxEndpointOperation> operationByName = mapOperations(
 | 
			
		||||
							endpoints.get("test").getOperations());
 | 
			
		||||
					assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
 | 
			
		||||
							"update", "getAnother");
 | 
			
		||||
							"update", "deleteSomething", "getAnother");
 | 
			
		||||
					JmxEndpointOperation getAll = operationByName.get("getAll");
 | 
			
		||||
					assertThat(getAll.getInvoker())
 | 
			
		||||
							.isInstanceOf(CachingOperationInvoker.class);
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +246,8 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
	private void assertJmxTestEndpoint(EndpointInfo<JmxEndpointOperation> endpoint) {
 | 
			
		||||
		Map<String, JmxEndpointOperation> operationByName = mapOperations(
 | 
			
		||||
				endpoint.getOperations());
 | 
			
		||||
		assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update");
 | 
			
		||||
		assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update",
 | 
			
		||||
				"deleteSomething");
 | 
			
		||||
		JmxEndpointOperation getAll = operationByName.get("getAll");
 | 
			
		||||
		assertThat(getAll.getDescription()).isEqualTo("Get all the things");
 | 
			
		||||
		assertThat(getAll.getOutputType()).isEqualTo(Object.class);
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +265,13 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
		assertThat(update.getParameters()).hasSize(2);
 | 
			
		||||
		hasDocumentedParameter(update, 0, "foo", String.class, "Foo identifier");
 | 
			
		||||
		hasDocumentedParameter(update, 1, "bar", String.class, "Bar value");
 | 
			
		||||
		JmxEndpointOperation deleteSomething = operationByName.get("deleteSomething");
 | 
			
		||||
		assertThat(deleteSomething.getDescription())
 | 
			
		||||
				.isEqualTo("Delete something based on a timeUnit");
 | 
			
		||||
		assertThat(deleteSomething.getOutputType()).isEqualTo(Void.TYPE);
 | 
			
		||||
		assertThat(deleteSomething.getParameters()).hasSize(1);
 | 
			
		||||
		hasDocumentedParameter(deleteSomething, 0, "unitMs", Long.class,
 | 
			
		||||
				"Number of milliseconds");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void hasDefaultParameter(JmxEndpointOperation operation, int index,
 | 
			
		||||
| 
						 | 
				
			
			@ -329,6 +344,11 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public void deleteSomething(TimeUnit timeUnit) {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "jmx", exposure = EndpointExposure.JMX)
 | 
			
		||||
| 
						 | 
				
			
			@ -367,6 +387,14 @@ public class JmxAnnotationEndpointDiscovererTests {
 | 
			
		|||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		@ManagedOperation(description = "Delete something based on a timeUnit")
 | 
			
		||||
		@ManagedOperationParameters({
 | 
			
		||||
				@ManagedOperationParameter(name = "unitMs", description = "Number of milliseconds") })
 | 
			
		||||
		public void deleteSomething(Long timeUnit) {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@JmxEndpointExtension(endpoint = TestEndpoint.class)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ import org.junit.Test;
 | 
			
		|||
 | 
			
		||||
import org.springframework.boot.endpoint.CachingConfiguration;
 | 
			
		||||
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
 | 
			
		||||
import org.springframework.boot.endpoint.DeleteOperation;
 | 
			
		||||
import org.springframework.boot.endpoint.Endpoint;
 | 
			
		||||
import org.springframework.boot.endpoint.OperationParameterMapper;
 | 
			
		||||
import org.springframework.boot.endpoint.ReadOperation;
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +160,23 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void deleteOperation() {
 | 
			
		||||
		load(TestEndpointConfiguration.class,
 | 
			
		||||
				(client) -> client.delete().uri("/test/one")
 | 
			
		||||
						.accept(MediaType.APPLICATION_JSON).exchange().expectStatus()
 | 
			
		||||
						.isOk().expectBody().jsonPath("part").isEqualTo("one"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void deleteOperationWithVoidResponse() {
 | 
			
		||||
		load(VoidDeleteResponseEndpointConfiguration.class, (context, client) -> {
 | 
			
		||||
			client.delete().uri("/voiddelete").accept(MediaType.APPLICATION_JSON).exchange()
 | 
			
		||||
					.expectStatus().isNoContent().expectBody().isEmpty();
 | 
			
		||||
			verify(context.getBean(EndpointDelegate.class)).delete();
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void nullIsPassedToTheOperationWhenArgumentIsNotFoundInPostRequestBody() {
 | 
			
		||||
		load(TestEndpointConfiguration.class, (context, client) -> {
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +206,14 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
						.isNotFound());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void nullResponseFromDeleteOperationResultsInNoContentResponseStatus() {
 | 
			
		||||
		load(NullDeleteResponseEndpointConfiguration.class,
 | 
			
		||||
				(context, client) -> client.delete().uri("/nulldelete")
 | 
			
		||||
						.accept(MediaType.APPLICATION_JSON).exchange().expectStatus()
 | 
			
		||||
						.isNoContent());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void nullResponseFromWriteOperationResultsInNoContentResponseStatus() {
 | 
			
		||||
		load(NullWriteResponseEndpointConfiguration.class,
 | 
			
		||||
| 
						 | 
				
			
			@ -308,6 +334,19 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@Import(BaseConfiguration.class)
 | 
			
		||||
	static class VoidDeleteResponseEndpointConfiguration {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public VoidDeleteResponseEndpoint voidDeleteResponseEndpoint(
 | 
			
		||||
				EndpointDelegate delegate) {
 | 
			
		||||
			return new VoidDeleteResponseEndpoint(delegate);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@Import(BaseConfiguration.class)
 | 
			
		||||
	static class NullWriteResponseEndpointConfiguration {
 | 
			
		||||
| 
						 | 
				
			
			@ -331,6 +370,17 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@Import(BaseConfiguration.class)
 | 
			
		||||
	static class NullDeleteResponseEndpointConfiguration {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public NullDeleteResponseEndpoint nullDeleteResponseEndpoint() {
 | 
			
		||||
			return new NullDeleteResponseEndpoint();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@Import(BaseConfiguration.class)
 | 
			
		||||
	static class ResourceEndpointConfiguration {
 | 
			
		||||
| 
						 | 
				
			
			@ -377,6 +427,11 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
			this.endpointDelegate.write(foo, bar);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public Map<String, Object> deletePart(@Selector String part) {
 | 
			
		||||
			return Collections.singletonMap("part", part);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "query")
 | 
			
		||||
| 
						 | 
				
			
			@ -421,6 +476,22 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "voiddelete")
 | 
			
		||||
	static class VoidDeleteResponseEndpoint {
 | 
			
		||||
 | 
			
		||||
		private final EndpointDelegate delegate;
 | 
			
		||||
 | 
			
		||||
		VoidDeleteResponseEndpoint(EndpointDelegate delegate) {
 | 
			
		||||
			this.delegate = delegate;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public void delete() {
 | 
			
		||||
			this.delegate.delete();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "nullwrite")
 | 
			
		||||
	static class NullWriteResponseEndpoint {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -448,6 +519,16 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "nulldelete")
 | 
			
		||||
	static class NullDeleteResponseEndpoint {
 | 
			
		||||
 | 
			
		||||
		@DeleteOperation
 | 
			
		||||
		public String deleteReturningNull() {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Endpoint(id = "resource")
 | 
			
		||||
	static class ResourceEndpoint {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -476,6 +557,8 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
 | 
			
		|||
 | 
			
		||||
		void write(String foo, String bar);
 | 
			
		||||
 | 
			
		||||
		void delete();
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue