mirror of https://github.com/halo-dev/halo
Add update and delete handlers for Extensions (#2172)
parent
b9e5ed2f4c
commit
d556787b3a
|
@ -2,8 +2,12 @@ package run.halo.app.extension;
|
||||||
|
|
||||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Objects;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
|
@ -28,11 +32,15 @@ public class ExtensionRouterFunctionFactory {
|
||||||
var getHandler = new ExtensionGetHandler(scheme, client);
|
var getHandler = new ExtensionGetHandler(scheme, client);
|
||||||
var listHandler = new ExtensionListHandler(scheme, client);
|
var listHandler = new ExtensionListHandler(scheme, client);
|
||||||
var createHandler = new ExtensionCreateHandler(scheme, client);
|
var createHandler = new ExtensionCreateHandler(scheme, client);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
var deleteHandler = new ExtensionDeleteHandler(scheme, client);
|
||||||
// TODO More handlers here
|
// TODO More handlers here
|
||||||
return route()
|
return route()
|
||||||
.GET(getHandler.pathPattern(), getHandler)
|
.GET(getHandler.pathPattern(), getHandler)
|
||||||
.GET(listHandler.pathPattern(), listHandler)
|
.GET(listHandler.pathPattern(), listHandler)
|
||||||
.POST(createHandler.pathPattern(), createHandler)
|
.POST(createHandler.pathPattern(), createHandler)
|
||||||
|
.PUT(updateHandler.pathPattern(), updateHandler)
|
||||||
|
.DELETE(deleteHandler.pathPattern(), deleteHandler)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +73,14 @@ public class ExtensionRouterFunctionFactory {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UpdateHandler extends HandlerFunction<ServerResponse>, PathPatternGenerator {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteHandler extends HandlerFunction<ServerResponse>, PathPatternGenerator {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class ExtensionCreateHandler implements CreateHandler {
|
static class ExtensionCreateHandler implements CreateHandler {
|
||||||
|
|
||||||
private final Scheme scheme;
|
private final Scheme scheme;
|
||||||
|
@ -82,16 +98,17 @@ public class ExtensionRouterFunctionFactory {
|
||||||
return request.bodyToMono(Unstructured.class)
|
return request.bodyToMono(Unstructured.class)
|
||||||
.switchIfEmpty(Mono.error(() -> new ExtensionConvertException(
|
.switchIfEmpty(Mono.error(() -> new ExtensionConvertException(
|
||||||
"Cannot read body to " + scheme.groupVersionKind())))
|
"Cannot read body to " + scheme.groupVersionKind())))
|
||||||
.doOnSuccess(client::create)
|
.flatMap(extToCreate -> Mono.fromCallable(() -> {
|
||||||
.map(unstructured ->
|
var name = extToCreate.getMetadata().getName();
|
||||||
client.fetch(scheme.type(), unstructured.getMetadata().getName())
|
client.create(extToCreate);
|
||||||
|
return client.fetch(scheme.type(), name)
|
||||||
.orElseThrow(() -> new ExtensionNotFoundException(
|
.orElseThrow(() -> new ExtensionNotFoundException(
|
||||||
scheme.groupVersionKind() + " " + unstructured.getMetadata().getName()
|
"Extension with name " + name + " was not found"));
|
||||||
+ "was not found")))
|
}))
|
||||||
.flatMap(extension -> ServerResponse
|
.flatMap(createdExt -> ServerResponse
|
||||||
.ok()
|
.created(URI.create(pathPattern() + "/" + createdExt.getMetadata().getName()))
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.bodyValue(extension))
|
.bodyValue(createdExt))
|
||||||
.cast(ServerResponse.class);
|
.cast(ServerResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,4 +176,81 @@ public class ExtensionRouterFunctionFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ExtensionUpdateHandler implements UpdateHandler {
|
||||||
|
|
||||||
|
private final Scheme scheme;
|
||||||
|
|
||||||
|
private final ExtensionClient client;
|
||||||
|
|
||||||
|
ExtensionUpdateHandler(Scheme scheme, ExtensionClient client) {
|
||||||
|
this.scheme = scheme;
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||||
|
String name = request.pathVariable("name");
|
||||||
|
return request.bodyToMono(Unstructured.class)
|
||||||
|
.filter(unstructured -> unstructured.getMetadata() != null
|
||||||
|
&& StringUtils.hasText(unstructured.getMetadata().getName())
|
||||||
|
&& Objects.equals(unstructured.getMetadata().getName(), name))
|
||||||
|
.switchIfEmpty(Mono.error(() -> new ExtensionConvertException(
|
||||||
|
"Cannot read body to " + scheme.groupVersionKind())))
|
||||||
|
.flatMap(extToUpdate -> Mono.fromCallable(() -> {
|
||||||
|
client.update(extToUpdate);
|
||||||
|
return client.fetch(scheme.type(), name)
|
||||||
|
.orElseThrow(() -> new ExtensionNotFoundException(
|
||||||
|
"Extension with name " + name + " was not found"));
|
||||||
|
}))
|
||||||
|
.flatMap(updated -> ServerResponse
|
||||||
|
.ok()
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(updated));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pathPattern() {
|
||||||
|
return PathPatternGenerator.buildExtensionPathPattern(scheme) + "/{name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ExtensionDeleteHandler implements DeleteHandler {
|
||||||
|
|
||||||
|
private final Scheme scheme;
|
||||||
|
|
||||||
|
private final ExtensionClient client;
|
||||||
|
|
||||||
|
ExtensionDeleteHandler(Scheme scheme, ExtensionClient client) {
|
||||||
|
this.scheme = scheme;
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||||
|
String name = request.pathVariable("name");
|
||||||
|
return getExtension(name)
|
||||||
|
.flatMap(extension ->
|
||||||
|
Mono.fromRunnable(() -> {
|
||||||
|
extension.getMetadata().setDeletionTimestamp(Instant.now());
|
||||||
|
client.update(extension);
|
||||||
|
}).thenReturn(extension))
|
||||||
|
.flatMap(extension -> this.getExtension(name))
|
||||||
|
.flatMap(extension -> ServerResponse
|
||||||
|
.ok()
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<? extends Extension> getExtension(String name) {
|
||||||
|
return Mono.justOrEmpty(client.fetch(scheme.type(), name))
|
||||||
|
.switchIfEmpty(Mono.error(() -> new ExtensionNotFoundException(
|
||||||
|
"Extension with name " + name + " was not found")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String pathPattern() {
|
||||||
|
return PathPatternGenerator.buildExtensionPathPattern(scheme) + "/{name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
package run.halo.app.config;
|
package run.halo.app.config;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.test.context.support.WithMockUser;
|
import org.springframework.security.test.context.support.WithMockUser;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import run.halo.app.core.extension.Role;
|
import run.halo.app.core.extension.Role;
|
||||||
import run.halo.app.core.extension.service.RoleService;
|
import run.halo.app.core.extension.service.RoleService;
|
||||||
|
import run.halo.app.extension.ExtensionClient;
|
||||||
import run.halo.app.extension.FakeExtension;
|
import run.halo.app.extension.FakeExtension;
|
||||||
import run.halo.app.extension.Metadata;
|
import run.halo.app.extension.Metadata;
|
||||||
import run.halo.app.extension.Scheme;
|
import run.halo.app.extension.Scheme;
|
||||||
|
@ -27,7 +32,6 @@ import run.halo.app.extension.SchemeManager;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@AutoConfigureWebTestClient
|
@AutoConfigureWebTestClient
|
||||||
@AutoConfigureTestDatabase
|
|
||||||
class ExtensionConfigurationTest {
|
class ExtensionConfigurationTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -50,17 +54,18 @@ class ExtensionConfigurationTest {
|
||||||
var role = new Role();
|
var role = new Role();
|
||||||
role.setRules(List.of(rule));
|
role.setRules(List.of(rule));
|
||||||
when(roleService.getRole(anyString())).thenReturn(role);
|
when(roleService.getRole(anyString())).thenReturn(role);
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
// register scheme
|
||||||
void cleanUp() {
|
schemeManager.register(FakeExtension.class);
|
||||||
schemeManager.fetch(Scheme.buildFromType(FakeExtension.class).groupVersionKind())
|
|
||||||
.ifPresent(schemeManager::unregister);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
void shouldReturnNotFoundWhenSchemeNotRegistered() {
|
void shouldReturnNotFoundWhenSchemeNotRegistered() {
|
||||||
|
// unregister the Extension if necessary
|
||||||
|
schemeManager.fetch(Scheme.buildFromType(FakeExtension.class).groupVersionKind())
|
||||||
|
.ifPresent(schemeManager::unregister);
|
||||||
|
|
||||||
webClient.get()
|
webClient.get()
|
||||||
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
||||||
.exchange()
|
.exchange()
|
||||||
|
@ -76,71 +81,158 @@ class ExtensionConfigurationTest {
|
||||||
.bodyValue(new FakeExtension())
|
.bodyValue(new FakeExtension())
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus().isNotFound();
|
.expectStatus().isNotFound();
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
webClient.put()
|
||||||
@WithMockUser
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/my-fake")
|
||||||
void shouldListExtensionsWhenSchemeRegistered() {
|
.bodyValue(new FakeExtension())
|
||||||
schemeManager.register(FakeExtension.class);
|
|
||||||
|
|
||||||
webClient.get()
|
|
||||||
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus().isOk();
|
.expectStatus().isNotFound();
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
webClient.delete()
|
||||||
@WithMockUser
|
|
||||||
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
|
|
||||||
void shouldCreateExtensionWhenSchemeRegistered() {
|
|
||||||
schemeManager.register(FakeExtension.class);
|
|
||||||
|
|
||||||
getCreateExtensionResponse()
|
|
||||||
.expectStatus().isOk()
|
|
||||||
.expectBody(FakeExtension.class)
|
|
||||||
.consumeWith(result -> {
|
|
||||||
var gotFake = result.getResponseBody();
|
|
||||||
assertNotNull(gotFake);
|
|
||||||
assertEquals("my-fake", gotFake.getMetadata().getName());
|
|
||||||
assertNotNull(gotFake.getMetadata().getVersion());
|
|
||||||
assertNotNull(gotFake.getMetadata().getCreationTimestamp());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithMockUser
|
|
||||||
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
|
|
||||||
void shouldGetExtensionWhenSchemeRegistered() {
|
|
||||||
schemeManager.register(FakeExtension.class);
|
|
||||||
|
|
||||||
// create the Extension
|
|
||||||
getCreateExtensionResponse().expectStatus().isOk();
|
|
||||||
|
|
||||||
webClient.get()
|
|
||||||
.uri("/apis/fake.halo.run/v1alpha1/fakes/my-fake")
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/my-fake")
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus().isOk()
|
.expectStatus().isNotFound();
|
||||||
.expectBody(FakeExtension.class)
|
|
||||||
.consumeWith(result -> {
|
|
||||||
var gotFake = result.getResponseBody();
|
|
||||||
assertNotNull(gotFake);
|
|
||||||
assertEquals("my-fake", gotFake.getMetadata().getName());
|
|
||||||
assertNotNull(gotFake.getMetadata().getVersion());
|
|
||||||
assertNotNull(gotFake.getMetadata().getCreationTimestamp());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebTestClient.ResponseSpec getCreateExtensionResponse() {
|
@Nested
|
||||||
var metadata = new Metadata();
|
@DisplayName("After creating extension")
|
||||||
metadata.setName("my-fake");
|
class AfterCreatingExtension {
|
||||||
var fake = new FakeExtension();
|
|
||||||
fake.setMetadata(metadata);
|
@Autowired
|
||||||
|
ExtensionClient extClient;
|
||||||
|
|
||||||
|
FakeExtension createdFake;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("my-fake");
|
||||||
|
var fake = new FakeExtension();
|
||||||
|
fake.setMetadata(metadata);
|
||||||
|
|
||||||
|
createdFake = webClient.post()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(fake)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isCreated()
|
||||||
|
.expectHeader().location("/apis/fake.halo.run/v1alpha1/fakes/my-fake")
|
||||||
|
.expectBody(FakeExtension.class)
|
||||||
|
.consumeWith(result -> {
|
||||||
|
var gotFake = result.getResponseBody();
|
||||||
|
assertNotNull(gotFake);
|
||||||
|
assertEquals("my-fake", gotFake.getMetadata().getName());
|
||||||
|
assertNotNull(gotFake.getMetadata().getVersion());
|
||||||
|
assertNotNull(gotFake.getMetadata().getCreationTimestamp());
|
||||||
|
})
|
||||||
|
.returnResult()
|
||||||
|
.getResponseBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void cleanUp() {
|
||||||
|
FakeExtension fakeToDelete = getFakeExtension(createdFake.getMetadata().getName());
|
||||||
|
extClient.delete(fakeToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void shouldDeleteExtensionWhenSchemeRegistered() {
|
||||||
|
webClient.delete()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/{name}",
|
||||||
|
createdFake.getMetadata().getName())
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.expectBody(FakeExtension.class)
|
||||||
|
.consumeWith(result -> {
|
||||||
|
var deletedFake = result.getResponseBody();
|
||||||
|
assertNotNull(deletedFake);
|
||||||
|
assertNotNull(deletedFake.getMetadata().getDeletionTimestamp());
|
||||||
|
assertTrue(deletedFake.getMetadata().getDeletionTimestamp()
|
||||||
|
.isBefore(Instant.now()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void shouldListExtensionsWhenSchemeRegistered() {
|
||||||
|
webClient.get()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBodyList(FakeExtension.class)
|
||||||
|
.hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void shouldUpdateExtensionWhenSchemeRegistered() {
|
||||||
|
var name = createdFake.getMetadata().getName();
|
||||||
|
FakeExtension fakeToUpdate = getFakeExtension(name);
|
||||||
|
fakeToUpdate.getMetadata().setLabels(Map.of("updated", "true"));
|
||||||
|
|
||||||
|
webClient.put()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/{name}", name)
|
||||||
|
.bodyValue(fakeToUpdate)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.expectBody(FakeExtension.class)
|
||||||
|
.consumeWith(result -> {
|
||||||
|
var updatedFake = result.getResponseBody();
|
||||||
|
assertNotNull(updatedFake);
|
||||||
|
assertNotEquals(fakeToUpdate.getMetadata().getVersion(),
|
||||||
|
updatedFake.getMetadata().getVersion());
|
||||||
|
assertEquals(Map.of("updated", "true"),
|
||||||
|
updatedFake.getMetadata().getLabels());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser
|
||||||
|
void shouldGetExtensionWhenSchemeRegistered() {
|
||||||
|
var name = createdFake.getMetadata().getName();
|
||||||
|
webClient.get()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/{name}", name)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBody(FakeExtension.class)
|
||||||
|
.consumeWith(result -> {
|
||||||
|
var gotFake = result.getResponseBody();
|
||||||
|
assertNotNull(gotFake);
|
||||||
|
assertEquals(name, gotFake.getMetadata().getName());
|
||||||
|
assertNotNull(gotFake.getMetadata().getVersion());
|
||||||
|
assertNotNull(gotFake.getMetadata().getCreationTimestamp());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeExtension getFakeExtension(String name) {
|
||||||
|
return webClient.get()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes/{name}", name)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBody(FakeExtension.class)
|
||||||
|
.returnResult()
|
||||||
|
.getResponseBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WebTestClient.ResponseSpec getCreateExtensionResponse() {
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("my-fake");
|
||||||
|
var fake = new FakeExtension();
|
||||||
|
fake.setMetadata(metadata);
|
||||||
|
|
||||||
|
return webClient.post()
|
||||||
|
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(fake)
|
||||||
|
.exchange();
|
||||||
|
}
|
||||||
|
|
||||||
return webClient.post()
|
|
||||||
.uri("/apis/fake.halo.run/v1alpha1/fakes")
|
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
|
||||||
.bodyValue(fake)
|
|
||||||
.exchange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,11 +2,16 @@ package run.halo.app.extension;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
@ -31,8 +36,8 @@ class ExtensionCreateHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
void shouldBuildPathPatternCorrectly() {
|
void shouldBuildPathPatternCorrectly() {
|
||||||
var scheme = Scheme.buildFromType(FakeExtension.class);
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
var getHandler = new ExtensionCreateHandler(scheme, client);
|
var createHandler = new ExtensionCreateHandler(scheme, client);
|
||||||
var pathPattern = getHandler.pathPattern();
|
var pathPattern = createHandler.pathPattern();
|
||||||
assertEquals("/apis/fake.halo.run/v1alpha1/fakes", pathPattern);
|
assertEquals("/apis/fake.halo.run/v1alpha1/fakes", pathPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +63,9 @@ class ExtensionCreateHandlerTest {
|
||||||
|
|
||||||
StepVerifier.create(responseMono)
|
StepVerifier.create(responseMono)
|
||||||
.consumeNextWith(response -> {
|
.consumeNextWith(response -> {
|
||||||
assertEquals(HttpStatus.OK, response.statusCode());
|
assertEquals(HttpStatus.CREATED, response.statusCode());
|
||||||
|
assertEquals("/apis/fake.halo.run/v1alpha1/fakes/my-fake",
|
||||||
|
response.headers().getLocation().toString());
|
||||||
assertEquals(MediaType.APPLICATION_JSON, response.headers().getContentType());
|
assertEquals(MediaType.APPLICATION_JSON, response.headers().getContentType());
|
||||||
assertTrue(response instanceof EntityResponse<?>);
|
assertTrue(response instanceof EntityResponse<?>);
|
||||||
assertEquals(fake, ((EntityResponse<?>) response).entity());
|
assertEquals(fake, ((EntityResponse<?>) response).entity());
|
||||||
|
@ -90,13 +97,16 @@ class ExtensionCreateHandlerTest {
|
||||||
|
|
||||||
var serverRequest = MockServerRequest.builder()
|
var serverRequest = MockServerRequest.builder()
|
||||||
.body(Mono.just(unstructured));
|
.body(Mono.just(unstructured));
|
||||||
when(client.fetch(eq(FakeExtension.class), eq("my-fake"))).thenReturn(Optional.empty());
|
doThrow(ExtensionNotFoundException.class).when(client).create(any());
|
||||||
|
|
||||||
var scheme = Scheme.buildFromType(FakeExtension.class);
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
var getHandler = new ExtensionCreateHandler(scheme, client);
|
var createHandler = new ExtensionCreateHandler(scheme, client);
|
||||||
var responseMono = getHandler.handle(serverRequest);
|
var responseMono = createHandler.handle(serverRequest);
|
||||||
|
|
||||||
StepVerifier.create(responseMono)
|
StepVerifier.create(responseMono)
|
||||||
.verifyError(ExtensionNotFoundException.class);
|
.verifyError(ExtensionNotFoundException.class);
|
||||||
|
verify(client, times(1)).create(
|
||||||
|
argThat(extension -> Objects.equals("my-fake", extension.getMetadata().getName())));
|
||||||
|
verify(client, times(0)).fetch(any(), anyString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package run.halo.app.extension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||||
|
import org.springframework.web.reactive.function.server.EntityResponse;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.test.StepVerifier;
|
||||||
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.ExtensionDeleteHandler;
|
||||||
|
import run.halo.app.extension.exception.ExtensionNotFoundException;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ExtensionDeleteHandlerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ExtensionClient client;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBuildPathPatternCorrectly() {
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var deleteHandler = new ExtensionDeleteHandler(scheme, client);
|
||||||
|
var pathPattern = deleteHandler.pathPattern();
|
||||||
|
assertEquals("/apis/fake.halo.run/v1alpha1/fakes/{name}", pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleCorrectly() {
|
||||||
|
final var fake = new FakeExtension();
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("my-fake");
|
||||||
|
fake.setMetadata(metadata);
|
||||||
|
|
||||||
|
var unstructured = new Unstructured();
|
||||||
|
unstructured.setMetadata(metadata);
|
||||||
|
unstructured.setApiVersion("fake.halo.run/v1alpha1");
|
||||||
|
unstructured.setKind("Fake");
|
||||||
|
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.pathVariable("name", "my-fake")
|
||||||
|
.body(Mono.just(unstructured));
|
||||||
|
when(client.fetch(eq(FakeExtension.class), eq("my-fake"))).thenReturn(Optional.of(fake));
|
||||||
|
doNothing().when(client).update(any());
|
||||||
|
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var deleteHandler = new ExtensionDeleteHandler(scheme, client);
|
||||||
|
var responseMono = deleteHandler.handle(serverRequest);
|
||||||
|
|
||||||
|
StepVerifier.create(responseMono)
|
||||||
|
.assertNext(response -> {
|
||||||
|
assertEquals(HttpStatus.OK, response.statusCode());
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON, response.headers().getContentType());
|
||||||
|
assertTrue(response instanceof EntityResponse<?>);
|
||||||
|
assertEquals(fake, ((EntityResponse<?>) response).entity());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
verify(client, times(2)).fetch(eq(FakeExtension.class), eq("my-fake"));
|
||||||
|
verify(client, times(1)).update(
|
||||||
|
argThat(fakeToDelete -> fakeToDelete.getMetadata().getDeletionTimestamp() != null));
|
||||||
|
verify(client, times(0)).delete(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnErrorWhenNoNameProvided() {
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.body(Mono.empty());
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var deleteHandler = new ExtensionDeleteHandler(scheme, client);
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> deleteHandler.handle(serverRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnErrorWhenExtensionNotFound() {
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.pathVariable("name", "my-fake")
|
||||||
|
.build();
|
||||||
|
when(client.fetch(FakeExtension.class, "my-fake")).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var deleteHandler = new ExtensionDeleteHandler(scheme, client);
|
||||||
|
var responseMono = deleteHandler.handle(serverRequest);
|
||||||
|
|
||||||
|
StepVerifier.create(responseMono)
|
||||||
|
.verifyError(ExtensionNotFoundException.class);
|
||||||
|
|
||||||
|
verify(client, times(1)).fetch(any(), anyString());
|
||||||
|
verify(client, times(0)).update(any());
|
||||||
|
verify(client, times(0)).delete(any());
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,20 +27,20 @@ class ExtensionListHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
void shouldBuildPathPatternCorrectly() {
|
void shouldBuildPathPatternCorrectly() {
|
||||||
var scheme = Scheme.buildFromType(FakeExtension.class);
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
var getHandler = new ExtensionListHandler(scheme, client);
|
var listHandler = new ExtensionListHandler(scheme, client);
|
||||||
var pathPattern = getHandler.pathPattern();
|
var pathPattern = listHandler.pathPattern();
|
||||||
assertEquals("/apis/fake.halo.run/v1alpha1/fakes", pathPattern);
|
assertEquals("/apis/fake.halo.run/v1alpha1/fakes", pathPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldHandleCorrectly() {
|
void shouldHandleCorrectly() {
|
||||||
var scheme = Scheme.buildFromType(FakeExtension.class);
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
var getHandler = new ExtensionListHandler(scheme, client);
|
var listHandler = new ExtensionListHandler(scheme, client);
|
||||||
var serverRequest = MockServerRequest.builder().build();
|
var serverRequest = MockServerRequest.builder().build();
|
||||||
final var fake = new FakeExtension();
|
final var fake = new FakeExtension();
|
||||||
when(client.list(eq(FakeExtension.class), any(), any())).thenReturn(List.of(fake));
|
when(client.list(eq(FakeExtension.class), any(), any())).thenReturn(List.of(fake));
|
||||||
|
|
||||||
var responseMono = getHandler.handle(serverRequest);
|
var responseMono = listHandler.handle(serverRequest);
|
||||||
|
|
||||||
StepVerifier.create(responseMono)
|
StepVerifier.create(responseMono)
|
||||||
.consumeNextWith(response -> {
|
.consumeNextWith(response -> {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||||
import run.halo.app.extension.ExtensionRouterFunctionFactory.CreateHandler;
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.CreateHandler;
|
||||||
import run.halo.app.extension.ExtensionRouterFunctionFactory.GetHandler;
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.GetHandler;
|
||||||
import run.halo.app.extension.ExtensionRouterFunctionFactory.ListHandler;
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.ListHandler;
|
||||||
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.UpdateHandler;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class ExtensionRouterFunctionFactoryTest {
|
class ExtensionRouterFunctionFactoryTest {
|
||||||
|
@ -53,10 +54,15 @@ class ExtensionRouterFunctionFactoryTest {
|
||||||
MockServerHttpRequest.post("/apis/fake.halo.run/v1alpha1/fakes").body("{}")
|
MockServerHttpRequest.post("/apis/fake.halo.run/v1alpha1/fakes").body("{}")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var updateWebExchange = MockServerWebExchange.from(
|
||||||
|
MockServerHttpRequest.put("/apis/fake.halo.run/v1alpha1/fakes/my-fake").body("{}")
|
||||||
|
);
|
||||||
|
|
||||||
return List.of(
|
return List.of(
|
||||||
new TestCase(listWebExchange, ListHandler.class),
|
new TestCase(listWebExchange, ListHandler.class),
|
||||||
new TestCase(getWebExchange, GetHandler.class),
|
new TestCase(getWebExchange, GetHandler.class),
|
||||||
new TestCase(createWebExchange, CreateHandler.class)
|
new TestCase(createWebExchange, CreateHandler.class),
|
||||||
|
new TestCase(updateWebExchange, UpdateHandler.class)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package run.halo.app.extension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||||
|
import org.springframework.web.reactive.function.server.EntityResponse;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.test.StepVerifier;
|
||||||
|
import run.halo.app.extension.ExtensionRouterFunctionFactory.ExtensionUpdateHandler;
|
||||||
|
import run.halo.app.extension.exception.ExtensionConvertException;
|
||||||
|
import run.halo.app.extension.exception.ExtensionNotFoundException;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ExtensionUpdateHandlerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ExtensionClient client;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldBuildPathPatternCorrectly() {
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
var pathPattern = updateHandler.pathPattern();
|
||||||
|
assertEquals("/apis/fake.halo.run/v1alpha1/fakes/{name}", pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleCorrectly() {
|
||||||
|
final var fake = new FakeExtension();
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("my-fake");
|
||||||
|
fake.setMetadata(metadata);
|
||||||
|
|
||||||
|
var unstructured = new Unstructured();
|
||||||
|
unstructured.setMetadata(metadata);
|
||||||
|
unstructured.setApiVersion("fake.halo.run/v1alpha1");
|
||||||
|
unstructured.setKind("Fake");
|
||||||
|
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.pathVariable("name", "my-fake")
|
||||||
|
.body(Mono.just(unstructured));
|
||||||
|
when(client.fetch(eq(FakeExtension.class), eq("my-fake"))).thenReturn(Optional.of(fake));
|
||||||
|
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
var responseMono = updateHandler.handle(serverRequest);
|
||||||
|
|
||||||
|
StepVerifier.create(responseMono)
|
||||||
|
.assertNext(response -> {
|
||||||
|
assertEquals(HttpStatus.OK, response.statusCode());
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON, response.headers().getContentType());
|
||||||
|
assertTrue(response instanceof EntityResponse<?>);
|
||||||
|
assertEquals(fake, ((EntityResponse<?>) response).entity());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
verify(client, times(1)).fetch(eq(FakeExtension.class), eq("my-fake"));
|
||||||
|
verify(client, times(1)).update(eq(unstructured));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnErrorWhenNoBodyProvided() {
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.pathVariable("name", "my-fake")
|
||||||
|
.body(Mono.empty());
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
var responseMono = updateHandler.handle(serverRequest);
|
||||||
|
StepVerifier.create(responseMono)
|
||||||
|
.verifyError(ExtensionConvertException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnErrorWhenNoNameProvided() {
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.body(Mono.empty());
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> updateHandler.handle(serverRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnErrorWhenExtensionNotFound() {
|
||||||
|
final var unstructured = new Unstructured();
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("my-fake");
|
||||||
|
unstructured.setMetadata(metadata);
|
||||||
|
unstructured.setApiVersion("fake.halo.run/v1alpha1");
|
||||||
|
unstructured.setKind("Fake");
|
||||||
|
|
||||||
|
var serverRequest = MockServerRequest.builder()
|
||||||
|
.pathVariable("name", "my-fake")
|
||||||
|
.body(Mono.just(unstructured));
|
||||||
|
doThrow(ExtensionNotFoundException.class).when(client).update(any());
|
||||||
|
|
||||||
|
var scheme = Scheme.buildFromType(FakeExtension.class);
|
||||||
|
var updateHandler = new ExtensionUpdateHandler(scheme, client);
|
||||||
|
var responseMono = updateHandler.handle(serverRequest);
|
||||||
|
|
||||||
|
StepVerifier.create(responseMono)
|
||||||
|
.verifyError(ExtensionNotFoundException.class);
|
||||||
|
|
||||||
|
verify(client, times(1)).update(
|
||||||
|
argThat(extension -> Objects.equals("my-fake", extension.getMetadata().getName())));
|
||||||
|
verify(client, times(0)).fetch(any(), anyString());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue