feat: allow attachment library to filter certain groups and their attachments (#4255)

#### What type of PR is this?

/kind feature
/area core

#### What this PR does / why we need it:

为附件库增加过滤条件,过滤 labels 中包含 `halo.run/hidden` 的分组及其附件。

#### Which issue(s) this PR fixes:

Fixes #4251 

#### Special notes for your reviewer:

保证默认情况下附件能够正常访问即可。
或者为分组增加 `halo.run/hidden` label,之后查看接口中是否不包含具有目标分组及其附件。

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/4270/head
Takagi 2023-07-20 15:28:17 +08:00 committed by GitHub
parent d21472dc0f
commit 5a7e794fea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 7 deletions

View File

@ -18,6 +18,7 @@ import run.halo.app.extension.GVK;
public class Group extends AbstractExtension {
public static final String KIND = "Group";
public static final String HIDDEN_LABEL = "halo.run/hidden";
@Schema(required = true)
private GroupSpec spec;

View File

@ -37,10 +37,12 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.attachment.Attachment;
import run.halo.app.core.extension.attachment.Group;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.core.extension.endpoint.SortResolver;
import run.halo.app.core.extension.service.AttachmentService;
import run.halo.app.extension.Comparators;
import run.halo.app.extension.MetadataUtil;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.router.IListRequest;
import run.halo.app.extension.router.IListRequest.QueryListRequest;
@ -104,12 +106,30 @@ public class AttachmentEndpoint implements CustomEndpoint {
Mono<ServerResponse> search(ServerRequest request) {
var searchRequest = new SearchRequest(request);
return client.list(Attachment.class,
searchRequest.toPredicate(), searchRequest.toComparator(),
searchRequest.getPage(), searchRequest.getSize())
.flatMap(listResult -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(listResult));
return client.list(Group.class, group -> MetadataUtil.nullSafeLabels(group)
.containsKey(Group.HIDDEN_LABEL), null)
.map(group -> group.getMetadata().getName())
.collectList()
.defaultIfEmpty(List.of())
.flatMap(groups -> client.list(Attachment.class,
searchRequest.toPredicate().and(visibleGroupPredicate(groups)),
searchRequest.toComparator(),
searchRequest.getPage(), searchRequest.getSize())
.flatMap(listResult -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(listResult)
)
);
}
static Predicate<Attachment> visibleGroupPredicate(List<String> hiddenGroups) {
return attachment -> {
if (!StringUtils.hasText(attachment.getSpec().getGroupName())) {
return true;
}
return !hiddenGroups.contains(attachment.getSpec().getGroupName());
};
}
public interface ISearchRequest extends IListRequest {

View File

@ -1,9 +1,11 @@
package run.halo.app.core.extension.attachment.endpoint;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@ -15,6 +17,7 @@ import static org.springframework.security.test.web.reactive.server.SecurityMock
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@ -28,9 +31,11 @@ import org.springframework.mock.web.reactive.function.server.MockServerRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.attachment.Attachment;
import run.halo.app.core.extension.attachment.Attachment.AttachmentSpec;
import run.halo.app.core.extension.attachment.Group;
import run.halo.app.core.extension.attachment.Policy;
import run.halo.app.core.extension.attachment.Policy.PolicySpec;
import run.halo.app.core.extension.attachment.endpoint.AttachmentEndpoint.SearchRequest;
@ -236,6 +241,9 @@ class AttachmentEndpointTest {
@Test
void shouldListUngroupedAttachments() {
when(client.list(eq(Group.class), any(), any()))
.thenReturn(Flux.empty());
when(client.list(same(Attachment.class), any(), any(), anyInt(), anyInt()))
.thenReturn(Mono.just(ListResult.emptyResult()));
@ -270,6 +278,47 @@ class AttachmentEndpointTest {
spec.setGroupName("halo");
assertFalse(pred.test(attachment));
}
@Test
void searchAttachmentWhenGroupIsEmpty() {
when(client.list(eq(Group.class), any(), any()))
.thenReturn(Flux.empty());
when(client.list(eq(Attachment.class), any(), any(), anyInt(), anyInt()))
.thenReturn(Mono.empty());
webClient
.get()
.uri("/attachments")
.exchange()
.expectStatus().isOk();
verify(client).list(eq(Attachment.class), any(), any(), anyInt(), anyInt());
}
}
@Test
void visibleGroupPredicate() {
Predicate<Attachment> noHiddenGroupPredicate =
AttachmentEndpoint.visibleGroupPredicate(List.of());
Attachment attachment = mock(Attachment.class);
AttachmentSpec spec = mock(AttachmentSpec.class);
when(attachment.getSpec()).thenReturn(spec);
when(spec.getGroupName()).thenReturn("");
assertThat(noHiddenGroupPredicate.test(attachment)).isTrue();
when(spec.getGroupName()).thenReturn("test");
assertThat(noHiddenGroupPredicate.test(attachment)).isTrue();
Predicate<Attachment> hasHiddenGroupPredicate =
AttachmentEndpoint.visibleGroupPredicate(List.of("hidden-group"));
when(spec.getGroupName()).thenReturn("fake");
assertThat(hasHiddenGroupPredicate.test(attachment)).isTrue();
when(spec.getGroupName()).thenReturn("hidden-group");
assertThat(hasHiddenGroupPredicate.test(attachment)).isFalse();
}
}

View File

@ -14,7 +14,11 @@ export function useFetchAttachmentGroup(): useFetchAttachmentGroupReturn {
queryKey: ["attachment-groups"],
queryFn: async () => {
const { data } =
await apiClient.extension.storage.group.liststorageHaloRunV1alpha1Group();
await apiClient.extension.storage.group.liststorageHaloRunV1alpha1Group(
{
labelSelector: ["!halo.run/hidden"],
}
);
return data.items;
},
refetchInterval(data) {