mirror of https://github.com/halo-dev/halo
refactor: add builder for list options (#6148)
#### What type of PR is this? /kind improvement /area core /milestone 2.17.x #### What this PR does / why we need it: 为 ListOptions 增加 Builder 以方便构建 ListOptions 对象,可以通过以下形式创建 ```java var listOptions = ListOptions.builder() .labelSelector()// 构建 LabelSelector .eq("key-1", "value-1") .notEq("key-2", "value-1") .exists("key-3") .end()// 结束构建 LabelSelector .fieldQuery(equal("spec.title", "fake-title")) // 构建 FieldSelector .andQuery(equal("spec.slug", "fake-slug")) .orQuery(equal("spec.slug", "test")) .build(); ``` #### Does this PR introduce a user-facing change? ```release-note 开发者相关:支持通过 Builder 来简化 ListOptions 的构建 ```pull/6131/head
parent
ba2987b585
commit
50f751dda2
|
@ -1,9 +1,13 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import run.halo.app.extension.index.query.Query;
|
||||
import run.halo.app.extension.index.query.QueryFactory;
|
||||
import run.halo.app.extension.router.selector.FieldSelector;
|
||||
import run.halo.app.extension.router.selector.LabelSelector;
|
||||
import run.halo.app.extension.router.selector.SelectorMatcher;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
|
@ -15,14 +19,109 @@ public class ListOptions {
|
|||
public String toString() {
|
||||
var sb = new StringBuilder();
|
||||
if (fieldSelector != null) {
|
||||
sb.append("fieldSelector: ").append(fieldSelector.query());
|
||||
var query = fieldSelector.query().toString();
|
||||
sb.append("fieldSelector: ")
|
||||
.append(query.startsWith("(") ? query : "(" + query + ")");
|
||||
}
|
||||
if (labelSelector != null) {
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("labelSelector: ").append(labelSelector);
|
||||
sb.append("labelSelector: (").append(labelSelector).append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static ListOptionsBuilder builder() {
|
||||
return new ListOptionsBuilder();
|
||||
}
|
||||
|
||||
public static ListOptionsBuilder builder(ListOptions listOptions) {
|
||||
return new ListOptionsBuilder(listOptions);
|
||||
}
|
||||
|
||||
public static class ListOptionsBuilder {
|
||||
private LabelSelectorBuilder labelSelectorBuilder;
|
||||
private Query query;
|
||||
|
||||
public ListOptionsBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new list options builder with the given list options.
|
||||
*/
|
||||
public ListOptionsBuilder(ListOptions listOptions) {
|
||||
if (listOptions.getLabelSelector() != null) {
|
||||
this.labelSelectorBuilder = new LabelSelectorBuilder(
|
||||
listOptions.getLabelSelector().getMatchers(), this);
|
||||
}
|
||||
if (listOptions.getFieldSelector() != null) {
|
||||
this.query = listOptions.getFieldSelector().query();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new label selector builder.
|
||||
*/
|
||||
public LabelSelectorBuilder labelSelector() {
|
||||
if (labelSelectorBuilder == null) {
|
||||
labelSelectorBuilder = new LabelSelectorBuilder(this);
|
||||
}
|
||||
return labelSelectorBuilder;
|
||||
}
|
||||
|
||||
public ListOptionsBuilder fieldQuery(Query query) {
|
||||
this.query = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* And the given query to the current query.
|
||||
*/
|
||||
public ListOptionsBuilder andQuery(Query query) {
|
||||
this.query = (this.query == null ? query : QueryFactory.and(this.query, query));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Or the given query to the current query.
|
||||
*/
|
||||
public ListOptionsBuilder orQuery(Query query) {
|
||||
this.query = (this.query == null ? query : QueryFactory.or(this.query, query));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list options.
|
||||
*/
|
||||
public ListOptions build() {
|
||||
var listOptions = new ListOptions();
|
||||
if (labelSelectorBuilder != null) {
|
||||
listOptions.setLabelSelector(labelSelectorBuilder.build());
|
||||
}
|
||||
if (query != null) {
|
||||
listOptions.setFieldSelector(FieldSelector.of(query));
|
||||
}
|
||||
return listOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LabelSelectorBuilder
|
||||
extends LabelSelector.LabelSelectorBuilder<LabelSelectorBuilder> {
|
||||
private final ListOptionsBuilder listOptionsBuilder;
|
||||
|
||||
public LabelSelectorBuilder(List<SelectorMatcher> givenMatchers,
|
||||
ListOptionsBuilder listOptionsBuilder) {
|
||||
super(givenMatchers);
|
||||
this.listOptionsBuilder = listOptionsBuilder;
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder(ListOptionsBuilder listOptionsBuilder) {
|
||||
this.listOptionsBuilder = listOptionsBuilder;
|
||||
}
|
||||
|
||||
public ListOptionsBuilder end() {
|
||||
return this.listOptionsBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
@ -24,6 +25,16 @@ public class LabelSelector implements Predicate<Map<String, String>> {
|
|||
.allMatch(matcher -> matcher.test(labels.get(matcher.getKey())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (matchers == null || matchers.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return matchers.stream()
|
||||
.map(SelectorMatcher::toString)
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new label selector that is the result of ANDing the current selector with the
|
||||
* given selector.
|
||||
|
@ -40,41 +51,58 @@ public class LabelSelector implements Predicate<Map<String, String>> {
|
|||
return labelSelector;
|
||||
}
|
||||
|
||||
public static LabelSelectorBuilder builder() {
|
||||
return new LabelSelectorBuilder();
|
||||
public static LabelSelectorBuilder<?> builder() {
|
||||
return new LabelSelectorBuilder<>();
|
||||
}
|
||||
|
||||
public static class LabelSelectorBuilder {
|
||||
public static class LabelSelectorBuilder<T extends LabelSelectorBuilder<T>> {
|
||||
private final List<SelectorMatcher> matchers = new ArrayList<>();
|
||||
|
||||
public LabelSelectorBuilder eq(String key, String value) {
|
||||
public LabelSelectorBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new label selector builder with the given matchers.
|
||||
*/
|
||||
public LabelSelectorBuilder(List<SelectorMatcher> givenMatchers) {
|
||||
if (givenMatchers != null) {
|
||||
matchers.addAll(givenMatchers);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T self() {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T eq(String key, String value) {
|
||||
matchers.add(EqualityMatcher.equal(key, value));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder notEq(String key, String value) {
|
||||
public T notEq(String key, String value) {
|
||||
matchers.add(EqualityMatcher.notEqual(key, value));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder in(String key, String... values) {
|
||||
public T in(String key, String... values) {
|
||||
matchers.add(SetMatcher.in(key, values));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder notIn(String key, String... values) {
|
||||
public T notIn(String key, String... values) {
|
||||
matchers.add(SetMatcher.notIn(key, values));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder exists(String key) {
|
||||
public T exists(String key) {
|
||||
matchers.add(SetMatcher.exists(key));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
public LabelSelectorBuilder notExists(String key) {
|
||||
public T notExists(String key) {
|
||||
matchers.add(SetMatcher.notExists(key));
|
||||
return this;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,6 +46,14 @@ public class SetMatcher implements SelectorMatcher {
|
|||
return operator.with(values).test(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (Operator.EXISTS.equals(operator) || Operator.NOT_EXISTS.equals(operator)) {
|
||||
return key + " " + operator;
|
||||
}
|
||||
return key + " " + operator + " (" + String.join(", ", values) + ")";
|
||||
}
|
||||
|
||||
private enum Operator {
|
||||
IN(values -> v -> contains(values, v)),
|
||||
NOT_IN(values -> v -> !contains(values, v)),
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package run.halo.app.extension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static run.halo.app.extension.index.query.QueryFactory.equal;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test for {@link ListOptions}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.17.0
|
||||
*/
|
||||
class ListOptionsTest {
|
||||
|
||||
@Nested
|
||||
class ListOptionsBuilderTest {
|
||||
|
||||
@Test
|
||||
void buildTest() {
|
||||
var listOptions = ListOptions.builder()
|
||||
.labelSelector()
|
||||
.eq("key-1", "value-1")
|
||||
.notEq("key-2", "value-1")
|
||||
.exists("key-3")
|
||||
.end()
|
||||
.andQuery(equal("spec.slug", "fake-slug"))
|
||||
.orQuery(equal("spec.slug", "test"))
|
||||
.build();
|
||||
System.out.println(listOptions);
|
||||
assertThat(listOptions.toString()).isEqualTo(
|
||||
"fieldSelector: (spec.slug = 'fake-slug' OR spec.slug = 'test'), labelSelector: "
|
||||
+ "(key-1 equal value-1, key-2 not_equal value-1, key-3 EXISTS)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildTest2() {
|
||||
var listOptions = ListOptions.builder()
|
||||
.labelSelector()
|
||||
.notEq("key-2", "value-1")
|
||||
.end()
|
||||
.fieldQuery(equal("spec.slug", "fake-slug"))
|
||||
.build();
|
||||
assertThat(listOptions.toString())
|
||||
.isEqualTo(
|
||||
"fieldSelector: (spec.slug = 'fake-slug'), labelSelector: (key-2 not_equal "
|
||||
+ "value-1)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package run.halo.app.extension.router.selector;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link LabelSelector}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.17.0
|
||||
*/
|
||||
class LabelSelectorTest {
|
||||
|
||||
@Test
|
||||
void builderTest() {
|
||||
var labelSelector = LabelSelector.builder()
|
||||
.eq("a", "v1")
|
||||
.in("b", "v2", "v3")
|
||||
.build();
|
||||
assertThat(labelSelector.toString())
|
||||
.isEqualTo("a equal v1, b IN (v2, v3)");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue