Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add builder for list options #6148

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 101 additions & 2 deletions api/src/main/java/run/halo/app/extension/ListOptions.java
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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;
Expand All @@ -24,6 +25,16 @@ public boolean test(@NonNull Map<String, String> labels) {
.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.
Expand All @@ -40,41 +51,58 @@ public LabelSelector and(LabelSelector other) {
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();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public boolean test(String s) {
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)),
Expand Down
51 changes: 51 additions & 0 deletions api/src/test/java/run/halo/app/extension/ListOptionsTest.java
Original file line number Diff line number Diff line change
@@ -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)");
}
}
}
Original file line number Diff line number Diff line change
@@ -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)");
}
}