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

feat: add index mechanism for extension #5121

Merged
merged 13 commits into from Jan 19, 2024
Merged
@@ -0,0 +1,57 @@
package run.halo.app.extension;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import run.halo.app.extension.index.query.QueryFactory;
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 final ExtensionClient client;
private final GroupVersionKind gvk;
private final LabelSelector labelSelector;
private final FieldSelector fieldSelector;

public static DefaultExtensionMatcherBuilder builder(ExtensionClient client,
GroupVersionKind gvk) {
return internalBuilder().client(client).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.equals(extension.groupVersionKind())) {
return false;
}
if (!hasFieldSelector() && !hasLabelSelector()) {
return true;
}
var listOptions = new ListOptions();
listOptions.setLabelSelector(labelSelector);
var fieldQuery = QueryFactory.all();
if (hasFieldSelector()) {
fieldQuery = QueryFactory.and(fieldQuery, fieldSelector.query());
}
listOptions.setFieldSelector(new FieldSelector(fieldQuery));
return client.indexedQueryEngine().retrieve(getGvk(),
listOptions, PageRequestImpl.ofSize(1)).getTotal() > 0;
}

boolean hasFieldSelector() {
return fieldSelector != null && fieldSelector.query() != null;
}

boolean hasLabelSelector() {
return labelSelector != null && !CollectionUtils.isEmpty(labelSelector.getMatchers());
}
}
9 changes: 9 additions & 0 deletions api/src/main/java/run/halo/app/extension/ExtensionClient.java
Expand Up @@ -4,6 +4,8 @@
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.springframework.data.domain.Sort;
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 +44,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, Sort sort);

<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 +89,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;
}
}
86 changes: 86 additions & 0 deletions api/src/main/java/run/halo/app/extension/PageRequestImpl.java
@@ -0,0 +1,86 @@
package run.halo.app.extension;

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

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(),
defaultIfNull(sort, Sort.unsorted()));
}

@Override
public boolean hasPrevious() {
return pageNumber > 1;
}
}
Expand Up @@ -2,8 +2,10 @@

import java.util.Comparator;
import java.util.function.Predicate;
import org.springframework.data.domain.Sort;
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 +41,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, Sort sort);

<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 +87,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);

}
7 changes: 7 additions & 0 deletions api/src/main/java/run/halo/app/extension/SchemeManager.java
Expand Up @@ -3,13 +3,17 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import org.springframework.lang.NonNull;
import run.halo.app.extension.exception.SchemeNotFoundException;
import run.halo.app.extension.index.IndexSpecs;

public interface SchemeManager {

void register(@NonNull Scheme scheme);

void register(@NonNull Scheme scheme, Consumer<IndexSpecs> specsConsumer);

/**
* Registers an Extension using its type.
*
Expand All @@ -20,6 +24,9 @@ default <T extends Extension> void register(Class<T> type) {
register(Scheme.buildFromType(type));
}

default <T extends Extension> void register(Class<T> type, Consumer<IndexSpecs> specsConsumer) {
register(Scheme.buildFromType(type), specsConsumer);
}

void unregister(@NonNull Scheme scheme);

Expand Down
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