Provide ungrouped query param to filter ungrouped attachments (#2752)

#### What type of PR is this?

/kind feature
/area core
/milestone 2.0.0-rc.1

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

Provide `ungrouped` query param to filter ungrouped attachments. Please note that we will ignore `group` query param when `ungropued` is `true`.

```bash
curl -X 'GET' \
  'http://localhost:8090/apis/api.console.halo.run/v1alpha1/attachments?ungrouped=true' \
  -H 'accept: */*'
```

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

Fixes https://github.com/halo-dev/halo/issues/2451

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

```release-note
None
```
pull/2760/head
John Niang 2022-11-24 15:43:02 +08:00 committed by GitHub
parent b0c461b5f6
commit f96ef7f1b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 10 deletions

View File

@ -4,6 +4,7 @@ import static java.util.Comparator.comparing;
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; import static org.springdoc.core.fn.builders.content.Builder.contentBuilder;
import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder; import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder;
import static org.springframework.boot.convert.ApplicationConversionService.getSharedInstance;
import static org.springframework.web.reactive.function.BodyExtractors.toMultipartData; import static org.springframework.web.reactive.function.BodyExtractors.toMultipartData;
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
import static run.halo.app.extension.ListResult.generateGenericClass; import static run.halo.app.extension.ListResult.generateGenericClass;
@ -168,6 +169,10 @@ public class AttachmentEndpoint implements CustomEndpoint {
@Schema(description = "Name of group") @Schema(description = "Name of group")
Optional<String> getGroup(); Optional<String> getGroup();
@Schema(description = "Filter attachments without group. This parameter will ignore group"
+ " parameter.")
Optional<Boolean> getUngrouped();
@Schema(description = "Name of user who uploaded the attachment") @Schema(description = "Name of user who uploaded the attachment")
Optional<String> getUploadedBy(); Optional<String> getUploadedBy();
@ -209,6 +214,12 @@ public class AttachmentEndpoint implements CustomEndpoint {
.filter(StringUtils::hasText); .filter(StringUtils::hasText);
} }
@Override
public Optional<Boolean> getUngrouped() {
return Optional.ofNullable(queryParams.getFirst("ungrouped"))
.map(ungroupedStr -> getSharedInstance().convert(ungroupedStr, Boolean.class));
}
@Override @Override
public Optional<String> getUploadedBy() { public Optional<String> getUploadedBy() {
return Optional.ofNullable(queryParams.getFirst("uploadedBy")) return Optional.ofNullable(queryParams.getFirst("uploadedBy"))
@ -221,33 +232,49 @@ public class AttachmentEndpoint implements CustomEndpoint {
} }
public Predicate<Attachment> toPredicate() { public Predicate<Attachment> toPredicate() {
var predicate = (Predicate<Attachment>) (attachment) -> getDisplayName() Predicate<Attachment> displayNamePred = attachment -> getDisplayName()
.map(displayNameInParam -> { .map(displayNameInParam -> {
String displayName = attachment.getSpec().getDisplayName(); String displayName = attachment.getSpec().getDisplayName();
return displayName.contains(displayNameInParam); return displayName.contains(displayNameInParam);
}).orElse(true) }).orElse(true);
&& getPolicy()
Predicate<Attachment> policyPred = attachment -> getPolicy()
.map(policy -> { .map(policy -> {
var policyRef = attachment.getSpec().getPolicyRef(); var policyRef = attachment.getSpec().getPolicyRef();
return policyRef != null && policy.equals(policyRef.getName()); return policyRef != null && policy.equals(policyRef.getName());
}).orElse(true) }).orElse(true);
&& getGroup()
Predicate<Attachment> groupPred = attachment -> getGroup()
.map(group -> { .map(group -> {
var groupRef = attachment.getSpec().getGroupRef(); var groupRef = attachment.getSpec().getGroupRef();
return groupRef != null && group.equals(groupRef.getName()); return groupRef != null && group.equals(groupRef.getName());
}) })
.orElse(true) .orElse(true);
&& getUploadedBy()
Predicate<Attachment> ungroupedPred = attachment -> getUngrouped()
.filter(Boolean::booleanValue)
.map(ungrouped -> {
var groupRef = attachment.getSpec().getGroupRef();
return groupRef == null || !StringUtils.hasText(groupRef.getName());
})
.orElseGet(() -> groupPred.test(attachment));
Predicate<Attachment> uploadedByPred = attachment -> getUploadedBy()
.map(uploadedBy -> { .map(uploadedBy -> {
var uploadedByRef = attachment.getSpec().getUploadedBy(); var uploadedByRef = attachment.getSpec().getUploadedBy();
return uploadedByRef != null && uploadedBy.equals(uploadedByRef.getName()); return uploadedByRef != null && uploadedBy.equals(uploadedByRef.getName());
}) })
.orElse(true); .orElse(true);
var selectorPredicate = labelAndFieldSelectorToPredicate(getLabelSelector(),
getFieldSelector());
return predicate.and(selectorPredicate); var selectorPred =
labelAndFieldSelectorToPredicate(getLabelSelector(), getFieldSelector());
return displayNamePred
.and(policyPred)
.and(ungroupedPred)
.and(uploadedByPred)
.and(selectorPred);
} }
public Comparator<Attachment> toComparator() { public Comparator<Attachment> toComparator() {

View File

@ -1,6 +1,10 @@
package run.halo.app.core.extension.attachment.endpoint; package run.halo.app.core.extension.attachment.endpoint;
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.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -18,13 +22,19 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder; import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
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.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.BodyInserters;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.core.extension.attachment.Attachment; import run.halo.app.core.extension.attachment.Attachment;
import run.halo.app.core.extension.attachment.Attachment.AttachmentSpec;
import run.halo.app.core.extension.attachment.Policy; import run.halo.app.core.extension.attachment.Policy;
import run.halo.app.core.extension.attachment.Policy.PolicySpec; import run.halo.app.core.extension.attachment.Policy.PolicySpec;
import run.halo.app.core.extension.attachment.endpoint.AttachmentEndpoint.SearchRequest;
import run.halo.app.extension.ConfigMap; import run.halo.app.extension.ConfigMap;
import run.halo.app.extension.ListResult;
import run.halo.app.extension.Metadata; import run.halo.app.extension.Metadata;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Ref; import run.halo.app.extension.Ref;
@ -149,4 +159,45 @@ class AttachmentEndpointTest {
} }
} }
@Nested
class SearchTest {
@Test
void shouldListUngroupedAttachments() {
when(client.list(same(Attachment.class), any(), any(), anyInt(), anyInt()))
.thenReturn(Mono.just(ListResult.emptyResult()));
webClient
.get()
.uri("/attachments")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("items.length()").isEqualTo(0);
}
@Test
void shouldFilterWithUngrouped() {
var httpRequest = MockServerHttpRequest.get("/attachments")
.build();
var exchange = new MockServerWebExchange.Builder(httpRequest)
.build();
MockServerRequest request = MockServerRequest.builder()
.queryParam("ungrouped", "true")
.queryParam("group", "halo")
.exchange(exchange)
.build();
var searchRequest = new SearchRequest(request);
var pred = searchRequest.toPredicate();
var attachment = new Attachment();
var spec = new AttachmentSpec();
attachment.setSpec(spec);
assertTrue(pred.test(attachment));
spec.setGroupRef(Ref.of("halo"));
assertFalse(pred.test(attachment));
}
}
} }