forked from halo-dev/halo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add index mechanism for extension (halo-dev#5121)
#### What type of PR is this? /kind feature /area core /milestone 2.12.x #### What this PR does / why we need it: 新增自定义模型索引机制 默认为所有的自定义模型都添加了以下索引: - metadata.name - metadata.labels - metadata.creationTimestamp - metadata.deletionTimestamp **how to test it?** 1. 测试应用的启动和停止 2. 测试 Reconciler 被正确执行,如创建文章发布文章,测试删除文章的某个 label 数据启动后能被 PostReconciler 恢复(即Reconciler 被正确执行) 3. 测试自定义模型自动生成的 list APIs 1. 能根据 labels 正确过滤数据和分页 2. 能根据 creationTimestamp 正确排序 3. 测试插件启用后也能正确使用 list APIs 根据 labels 过滤数据和 creationTimestamp 排序 4. 能正确删除数据(则表示 GcReconciler 使用索引正确) 5. 测试在插件中为自定义模型注册索引 ```java public class DemoPlugin extension BasePlugin { private final SchemeManager schemeManager; public MomentsPlugin(PluginContext pluginContext, SchemeManager schemeManager) { super(pluginContext); this.schemeManager = schemeManager; } @OverRide public void start() { schemeManager.register(Moment.class, indexSpecs -> { indexSpecs.add(new IndexSpec() .setName("spec.tags") .setIndexFunc(multiValueAttribute(Moment.class, moment -> { var tags = moment.getSpec().getTags(); return tags == null ? Set.of() : tags; })) ); indexSpecs.add(new IndexSpec() .setName("spec.owner") .setIndexFunc(simpleAttribute(Moment.class, moment -> moment.getSpec().getOwner()) ) ); indexSpecs.add(new IndexSpec() .setName("spec.releaseTime") .setIndexFunc(simpleAttribute(Moment.class, moment -> { var releaseTime = moment.getSpec().getReleaseTime(); return releaseTime == null ? null : releaseTime.toString(); })) ); indexSpecs.add(new IndexSpec() .setName("spec.visible") .setIndexFunc(simpleAttribute(Moment.class, moment -> { var visible = moment.getSpec().getVisible(); return visible == null ? null : visible.toString(); })) ); }); } @OverRide public void stop() { // unregister scheme 即可,不需要手动删除索引 } } ``` 可以正确在自动生成的 list APIs 使用 fieldSelector 来过滤 `spec.slug` 和排序,可以自己添加其他的 indexSpec 测试 6. 测试唯一索引并添加重复数据,期望无法添加进去 #### Which issue(s) this PR fixes: Fixes halo-dev#5058 #### Does this PR introduce a user-facing change? ```release-note 新增自定义模型索引机制 ```
- Loading branch information
Showing
115 changed files
with
6,609 additions
and
170 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
api/src/main/java/run/halo/app/extension/DefaultExtensionMatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
api/src/main/java/run/halo/app/extension/ExtensionMatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
86
api/src/main/java/run/halo/app/extension/PageRequestImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.