Skip to content

Commit

Permalink
feat: add index mechanism for extension
Browse files Browse the repository at this point in the history
  • Loading branch information
guqing committed Jan 3, 2024
1 parent 285ac6a commit 50a95d2
Show file tree
Hide file tree
Showing 89 changed files with 5,031 additions and 160 deletions.
@@ -0,0 +1,54 @@
package run.halo.app.extension;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

import java.util.Map;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.extension.router.selector.LabelSelector;

@Getter
@RequiredArgsConstructor
@Builder(builderMethodName = "internalBuilder")
public class DefaultExtensionMatcher implements ExtensionMatcher {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();

private final GroupVersionKind gvk;
private final LabelSelector labelSelector;
private final FieldSelector fieldSelector;

public static DefaultExtensionMatcherBuilder builder(GroupVersionKind gvk) {
return internalBuilder().gvk(gvk);
}

/**
* Match the given extension with the current matcher.
*
* @param extension extension to match
* @return true if the extension matches the current matcher
*/
@Override
public boolean match(Extension extension) {
if (gvk != null && !gvk.equals(extension.groupVersionKind())) {
return false;
}
var labels = defaultIfNull(extension.getMetadata().getLabels(), Map.<String, String>of());
if (labelSelector != null && !labelSelector.test(labels)) {
return false;
}

if (fieldSelector != null) {
for (var matcher : fieldSelector.getMatchers()) {
var fieldValue = PARSER.parseRaw(matcher.getKey())
.getValue(extension, String.class);
if (!matcher.test(fieldValue)) {
return false;
}
}
}
return true;
}
}
8 changes: 8 additions & 0 deletions api/src/main/java/run/halo/app/extension/ExtensionClient.java
Expand Up @@ -4,6 +4,7 @@
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import run.halo.app.extension.index.IndexedQueryEngine;

/**
* ExtensionClient is an interface which contains some operations on Extension instead of
Expand Down Expand Up @@ -42,6 +43,11 @@ <E extends Extension> List<E> list(Class<E> type, Predicate<E> predicate,
<E extends Extension> ListResult<E> list(Class<E> type, Predicate<E> predicate,
Comparator<E> comparator, int page, int size);

<E extends Extension> List<E> listAll(Class<E> type, ListOptions options);

<E extends Extension> ListResult<E> listBy(Class<E> type, ListOptions options,
PageRequest page);

/**
* Fetches Extension by its type and name.
*
Expand Down Expand Up @@ -82,6 +88,8 @@ <E extends Extension> ListResult<E> list(Class<E> type, Predicate<E> predicate,
*/
<E extends Extension> void delete(E extension);

IndexedQueryEngine indexedQueryEngine();

void watch(Watcher watcher);

}
14 changes: 14 additions & 0 deletions api/src/main/java/run/halo/app/extension/ExtensionMatcher.java
@@ -0,0 +1,14 @@
package run.halo.app.extension;

import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.extension.router.selector.LabelSelector;

public interface ExtensionMatcher {
GroupVersionKind getGvk();

LabelSelector getLabelSelector();

FieldSelector getFieldSelector();

boolean match(Extension extension);
}
13 changes: 13 additions & 0 deletions api/src/main/java/run/halo/app/extension/ListOptions.java
@@ -0,0 +1,13 @@
package run.halo.app.extension;

import lombok.Data;
import lombok.experimental.Accessors;
import run.halo.app.extension.router.selector.FieldSelector;
import run.halo.app.extension.router.selector.LabelSelector;

@Data
@Accessors(chain = true)
public class ListOptions {
private LabelSelector labelSelector;
private FieldSelector fieldSelector;
}
65 changes: 65 additions & 0 deletions api/src/main/java/run/halo/app/extension/PageRequest.java
@@ -0,0 +1,65 @@
package run.halo.app.extension;

import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;

/**
* <p>{@link PageRequest} is an interface for pagination information.</p>
* <p>Page number starts from 1.</p>
* <p>if page size is 0, it means no pagination and all results will be returned.</p>
*
* @author guqing
* @see PageRequestImpl
* @since 2.12.0
*/
public interface PageRequest {
int getPageNumber();

int getPageSize();

PageRequest previous();

PageRequest next();

/**
* Returns the previous {@link PageRequest} or the first {@link PageRequest} if the current one
* already is the first one.
*
* @return a new {@link org.springframework.data.domain.PageRequest} with
* {@link #getPageNumber()} - 1 as {@link #getPageNumber()}
*/
PageRequest previousOrFirst();

/**
* Returns the {@link PageRequest} requesting the first page.
*
* @return a new {@link org.springframework.data.domain.PageRequest} with
* {@link #getPageNumber()} = 1 as {@link #getPageNumber()}
*/
PageRequest first();

/**
* Creates a new {@link PageRequest} with {@code pageNumber} applied.
*
* @param pageNumber 1-based page index.
* @return a new {@link org.springframework.data.domain.PageRequest}
*/
PageRequest withPage(int pageNumber);

PageRequestImpl withSort(Sort sort);

boolean hasPrevious();

Sort getSort();

/**
* Returns the current {@link Sort} or the given one if the current one is unsorted.
*
* @param sort must not be {@literal null}.
* @return the current {@link Sort} or the given one if the current one is unsorted.
*/
default Sort getSortOr(Sort sort) {
Assert.notNull(sort, "Fallback Sort must not be null");
return getSort().isSorted() ? getSort() : sort;
}
}
83 changes: 83 additions & 0 deletions api/src/main/java/run/halo/app/extension/PageRequestImpl.java
@@ -0,0 +1,83 @@
package run.halo.app.extension;

import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;

public class PageRequestImpl implements PageRequest {

private final int pageNumber;
private final int pageSize;
private final Sort sort;

public PageRequestImpl(int pageNumber, int pageSize, Sort sort) {
Assert.notNull(sort, "Sort must not be null");
Assert.isTrue(pageNumber >= 0, "Page index must not be less than zero!");
Assert.isTrue(pageSize >= 0, "Page size must not be less than one!");
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.sort = sort;
}

public static PageRequestImpl of(int pageNumber, int pageSize) {
return of(pageNumber, pageSize, Sort.unsorted());
}

public static PageRequestImpl of(int pageNumber, int pageSize, Sort sort) {
return new PageRequestImpl(pageNumber, pageSize, sort);
}

public static PageRequestImpl ofSize(int pageSize) {
return PageRequestImpl.of(1, pageSize);
}

@Override
public int getPageNumber() {
return pageNumber;
}

@Override
public int getPageSize() {
return pageSize;
}

@Override
public PageRequest previous() {
return getPageNumber() == 0 ? this
: new PageRequestImpl(getPageNumber() - 1, getPageSize(), getSort());
}

@Override
public Sort getSort() {
return sort;
}

@Override
public PageRequest next() {
return new PageRequestImpl(getPageNumber() + 1, getPageSize(), getSort());
}

@Override
public PageRequest previousOrFirst() {
return hasPrevious() ? previous() : first();
}

@Override
public PageRequest first() {
return new PageRequestImpl(1, getPageSize(), getSort());
}

@Override
public PageRequest withPage(int pageNumber) {
return new PageRequestImpl(pageNumber, getPageSize(), getSort());
}

@Override
public PageRequestImpl withSort(Sort sort) {
return new PageRequestImpl(getPageNumber(), getPageSize(), sort);
}

@Override
public boolean hasPrevious() {
return pageNumber > 1;
}
}
Expand Up @@ -4,6 +4,7 @@
import java.util.function.Predicate;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.extension.index.IndexedQueryEngine;

/**
* ExtensionClient is an interface which contains some operations on Extension instead of
Expand Down Expand Up @@ -39,6 +40,11 @@ <E extends Extension> Flux<E> list(Class<E> type, Predicate<E> predicate,
<E extends Extension> Mono<ListResult<E>> list(Class<E> type, Predicate<E> predicate,
Comparator<E> comparator, int page, int size);

<E extends Extension> Flux<E> listAll(Class<E> type, ListOptions options);

<E extends Extension> Mono<ListResult<E>> listBy(Class<E> type, ListOptions options,
PageRequest pageable);

/**
* Fetches Extension by its type and name.
*
Expand Down Expand Up @@ -80,6 +86,8 @@ <E extends Extension> Mono<ListResult<E>> list(Class<E> type, Predicate<E> predi
*/
<E extends Extension> Mono<E> delete(E extension);

IndexedQueryEngine indexedQueryEngine();

void watch(Watcher watcher);

}
5 changes: 5 additions & 0 deletions api/src/main/java/run/halo/app/extension/Watcher.java
Expand Up @@ -3,9 +3,14 @@
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import reactor.core.Disposable;
import run.halo.app.extension.controller.Reconciler;

public interface Watcher extends Disposable {

default void onAdd(Reconciler.Request request) {
// Do nothing here, just for sync all on start.
}

default void onAdd(Extension extension) {
// Do nothing here
}
Expand Down
@@ -0,0 +1,48 @@
package run.halo.app.extension;

import java.util.Objects;
import lombok.Builder;

public class WatcherExtensionMatchers {
private final GroupVersionKind gvk;
private final ExtensionMatcher onAddMatcher;
private final ExtensionMatcher onUpdateMatcher;
private final ExtensionMatcher onDeleteMatcher;

/**
* Constructs a new {@link WatcherExtensionMatchers} with the given
* {@link DefaultExtensionMatcher}.
*/
@Builder(builderMethodName = "internalBuilder")
public WatcherExtensionMatchers(GroupVersionKind gvk, ExtensionMatcher onAddMatcher,
ExtensionMatcher onUpdateMatcher, ExtensionMatcher onDeleteMatcher) {
this.gvk = gvk;
this.onAddMatcher = Objects.requireNonNullElse(onAddMatcher, emptyMatcher(gvk));
this.onUpdateMatcher = Objects.requireNonNullElse(onUpdateMatcher, emptyMatcher(gvk));
this.onDeleteMatcher = Objects.requireNonNullElse(onDeleteMatcher, emptyMatcher(gvk));
}

public GroupVersionKind getGroupVersionKind() {
return this.gvk;
}

public ExtensionMatcher onAddMatcher() {
return this.onAddMatcher;
}

public ExtensionMatcher onUpdateMatcher() {
return this.onUpdateMatcher;
}

public ExtensionMatcher onDeleteMatcher() {
return this.onDeleteMatcher;
}

public static WatcherExtensionMatchersBuilder builder(GroupVersionKind gvk) {
return internalBuilder().gvk(gvk);
}

static ExtensionMatcher emptyMatcher(GroupVersionKind gvk) {
return DefaultExtensionMatcher.builder(gvk).build();
}
}

0 comments on commit 50a95d2

Please sign in to comment.