Skip to content

Commit

Permalink
feat: display subgroups in plugin home page
Browse files Browse the repository at this point in the history
  • Loading branch information
Skraye committed Apr 25, 2024
1 parent a3dbb8c commit 93f9f70
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 24 deletions.
25 changes: 17 additions & 8 deletions core/src/main/java/io/kestra/core/docs/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.kestra.core.models.annotations.PluginSubGroup;
import io.kestra.core.plugins.RegisteredPlugin;
import io.micronaut.core.annotation.Nullable;
import lombok.Data;
import lombok.NoArgsConstructor;

Expand Down Expand Up @@ -29,11 +30,16 @@ public class Plugin {
private List<String> taskRunners;
private List<String> guides;
private List<PluginSubGroup.PluginCategory> categories;
private String subGroup;

public static Plugin of(RegisteredPlugin registeredPlugin) {
public static Plugin of(RegisteredPlugin registeredPlugin, @Nullable String subgroup) {
Plugin plugin = new Plugin();
plugin.name = registeredPlugin.name();
plugin.title = registeredPlugin.title();
if (subgroup == null) {
plugin.title = registeredPlugin.title();
} else {
plugin.title = subgroup.substring(subgroup.lastIndexOf('.') + 1);
}
plugin.group = registeredPlugin.group();
plugin.description = registeredPlugin.description();
plugin.longDescription = registeredPlugin.longDescription();
Expand All @@ -58,12 +64,15 @@ public static Plugin of(RegisteredPlugin registeredPlugin) {
.distinct()
.toList();

plugin.tasks = filterAndGetClassName(registeredPlugin.getTasks());
plugin.triggers = filterAndGetClassName(registeredPlugin.getTriggers());
plugin.conditions = filterAndGetClassName(registeredPlugin.getConditions());
plugin.storages = filterAndGetClassName(registeredPlugin.getStorages());
plugin.secrets = filterAndGetClassName(registeredPlugin.getSecrets());
plugin.taskRunners = filterAndGetClassName(registeredPlugin.getTaskRunners());
plugin.subGroup = subgroup;

plugin.tasks = filterAndGetClassName(registeredPlugin.getTasks()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();
plugin.triggers = filterAndGetClassName(registeredPlugin.getTriggers()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();
plugin.conditions = filterAndGetClassName(registeredPlugin.getConditions()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();
plugin.storages = filterAndGetClassName(registeredPlugin.getStorages()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();
plugin.secrets = filterAndGetClassName(registeredPlugin.getSecrets()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();
plugin.taskRunners = filterAndGetClassName(registeredPlugin.getTaskRunners()).stream().filter(c -> subgroup == null || c.startsWith(subgroup)).toList();


return plugin;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@PluginSubGroup(categories = { PluginSubGroup.PluginCategory.STORAGE, PluginSubGroup.PluginCategory.CORE})
package io.kestra.core.models.triggers.types;

import io.kestra.core.models.annotations.PluginSubGroup;
26 changes: 26 additions & 0 deletions core/src/main/java/io/kestra/core/plugins/RegisteredPlugin.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.kestra.core.plugins;

import io.kestra.core.models.annotations.PluginSubGroup;
import io.kestra.core.models.conditions.Condition;
import io.kestra.core.models.tasks.runners.TaskRunner;
import io.kestra.core.models.tasks.Task;
Expand Down Expand Up @@ -114,6 +115,31 @@ public Map<String, List<Class>> allClassGrouped() {
return result;
}

// public Map<String, Map<String,List<Class>>> allClassGroupedBySubGroup() {
//
// }

public Set<String> subGroupNames() {
return allClass()
.stream()
.map(clazz -> {
var pluginSubGroup = clazz.getPackage().getDeclaredAnnotation(PluginSubGroup.class);

// some plugins declare subgroup for main plugins
if (clazz.getPackageName().length() == this.group().length()) {
pluginSubGroup = null;
}

if (pluginSubGroup != null && clazz.getPackageName().startsWith(this.group()) ) {
return this.group() + "." + clazz.getPackageName().substring(this.group().length() + 1);
} else {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}

public String name() {
return ObjectUtils.firstNonNull(
this.getManifest() == null ? "core" : null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@PluginSubGroup(categories = { PluginSubGroup.PluginCategory.CORE})
package io.kestra.core.tasks.outputs;

import io.kestra.core.models.annotations.PluginSubGroup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@PluginSubGroup(categories = { PluginSubGroup.PluginCategory.CORE})
package io.kestra.core.tasks.templating;

import io.kestra.core.models.annotations.PluginSubGroup;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions ui/src/components/plugins/Plugin.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<top-nav-bar :title="routeInfo.title" />
<top-nav-bar :title="routeInfo.title" :breadcrumb="routeInfo.breadcrumb" />
<section :class="pluginIsSelected ? 'mt-4': ''">
<el-row :gutter="15">
<el-col :span="4" v-if="pluginIsSelected">
<Toc @router-change="onRouterChange" v-if="plugins" :plugins="plugins" />
<Toc @router-change="onRouterChange" v-if="plugins" :plugins="plugins.filter(p => !p.subGroup)" />
</el-col>
<el-col :span="(pluginIsSelected) ? 18 : 24" class="markdown" v-loading="isLoading">
<markdown v-if="pluginIsSelected" :source="plugin.markdown" :permalink="true" />
Expand Down Expand Up @@ -68,7 +68,7 @@
},
methods: {
loadToc() {
this.$store.dispatch("plugin/list")
this.$store.dispatch("plugin/listWithSubgroup")
},
loadPlugin() {
Expand Down
25 changes: 21 additions & 4 deletions ui/src/components/plugins/PluginHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</div>
</template>
<div v-if="isVisible(plugin)" class="plugin-card" @click="openGroup(plugin)">
<task-icon class="size" :only-icon="true" :cls="plugin.group" :icons="icons" />
<task-icon class="size" :only-icon="true" :cls="hasIcon(plugin.subGroup) ? plugin.subGroup : plugin.group" :icons="icons" />
<span class="text-truncate">{{ plugin.title.capitalize() }}</span>
</div>
</el-tooltip>
Expand Down Expand Up @@ -103,9 +103,23 @@
},
computed: {
countPlugin() {
return this.plugins.reduce((acc, plugin) => {
return acc + plugin.tasks.length + plugin.triggers.length + plugin.conditions.length + plugin.taskRunners.length
}, 0)
let allTasks = [];
let allTriggers = [];
let allConditions = [];
let allTaskRunners = [];
// avoid duplicate across groups and subgroups
this.plugins.forEach(plugin => {
allTasks = [...allTasks, ...plugin.tasks];
allTriggers = [...allTriggers, ...plugin.triggers];
allConditions = [...allConditions, ...plugin.conditions];
allTaskRunners = [...allTaskRunners, ...plugin.taskRunners];
});
return (new Set(allTasks)).size +
(new Set(allTriggers)).size +
(new Set(allConditions)).size +
(new Set(allTaskRunners)).size;
},
pluginsList() {
return this.plugins
Expand Down Expand Up @@ -140,6 +154,9 @@
},
isVisible(plugin) {
return [...plugin.tasks, ...plugin.triggers, ...plugin.conditions, ...plugin.taskRunners].length > 0
},
hasIcon(cls) {
return this.icons[cls] !== undefined;
}
}
Expand Down
7 changes: 7 additions & 0 deletions ui/src/stores/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export default {
return response.data;
})
},
listWithSubgroup({commit}) {
return this.$http.get(`${apiUrl(this)}/plugins/groups/subgroups`, {}).then(response => {
commit("setPlugins", response.data)
commit("setPluginSingleList", response.data.map(plugin => plugin.tasks.concat(plugin.triggers, plugin.conditions, plugin.controllers, plugin.storages, plugin.taskRunners)).flat())
return response.data;
})
},
load({commit, state}, options) {
if (options.cls === undefined) {
throw new Error("missing required cls");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ protected ClassInputDocumentation inputDocumentation(Type type) throws ClassNotF
public List<Plugin> search() {
return pluginRegistry.plugins()
.stream()
.map(Plugin::of)
.map(p -> Plugin.of(p, null))
.collect(Collectors.toList());
}

Expand Down Expand Up @@ -159,15 +159,21 @@ public MutableHttpResponse<Map<String, PluginIcon>> icons() {
@ExecuteOn(TaskExecutors.IO)
@Operation(tags = {"Plugins"}, summary = "Get plugins icons")
public MutableHttpResponse<Map<String, PluginIcon>> pluginGroupIcons() {
Map<String, PluginIcon> icons = pluginRegistry
.plugins()
.stream()
Map<String, PluginIcon> icons = new HashMap<>();

pluginRegistry.plugins().stream()
.filter(plugin -> plugin.group() != null)
.collect(Collectors.toMap(
RegisteredPlugin::group,
plugin -> new PluginIcon("plugin-icon", plugin.icon("plugin-icon"), false),
(a1, a2) -> a1
));
.forEach(plugin -> {
String group = plugin.group();
if (group != null) {
icons.put(group, new PluginIcon("plugin-icon", plugin.icon("plugin-icon"), false));
}

plugin.subGroupNames().forEach(subgroup -> {
icons.put(subgroup, new PluginIcon("plugin-icon", plugin.icon(subgroup), false));
});
});

return HttpResponse.ok(icons).header(HttpHeaders.CACHE_CONTROL, CACHE_DIRECTIVE);
}

Expand Down Expand Up @@ -199,6 +205,26 @@ public HttpResponse<DocumentationWithSchema> pluginDocumentation(
.header(HttpHeaders.CACHE_CONTROL, CACHE_DIRECTIVE);
}

@Get("/groups/subgroups")
@ExecuteOn(TaskExecutors.IO)
@Operation(tags = {"Plugins"}, summary = "Get plugins group by subgroups")
public List<Plugin> subgroups() {
return Stream.concat(
pluginRegistry.plugins()
.stream()
.map(p -> Plugin.of(p, null)),
pluginRegistry.plugins()
.stream()
.flatMap(p -> p.subGroupNames()
.stream()
.map(subgroup -> Plugin.of(p, subgroup))
)
)
.distinct()
.toList();
}


@SuppressWarnings({"rawtypes", "unchecked"})
@Cacheable("default")
protected ClassPluginDocumentation<?> pluginDocumentation(List<RegisteredPlugin> plugins, String className, Boolean allProperties) {
Expand Down

0 comments on commit 93f9f70

Please sign in to comment.