diff --git a/packages/velaux-data/src/plugins/app.ts b/packages/velaux-data/src/plugins/app.ts index fee744bc7..93d2c75fb 100644 --- a/packages/velaux-data/src/plugins/app.ts +++ b/packages/velaux-data/src/plugins/app.ts @@ -6,14 +6,38 @@ export interface AppPluginMeta extends PluginMeta // TODO anything specific to apps? } -export interface AppRootProps { +export interface PluginRootProps{ meta: AppPluginMeta; +} + +export interface PageRootProps extends PluginRootProps{ /** * base URL segment for an app, /app/pluginId */ basename: string; // The URL path to this page } +export interface DefinitionRootProps extends PluginRootProps { + // form props + "data-meta"?: string; + + project?: string; + + id?: string; + + onChange?: Function; + + registerForm?: Function; + + value?: any; + + ref?: any; +} + +export interface AppRootProps extends DefinitionRootProps, PageRootProps{ + +} + export class AppPagePlugin extends VelaUXPlugin> { // Content under: /a/${plugin-id}/* root?: ComponentType>; diff --git a/packages/velaux-data/src/plugins/types.ts b/packages/velaux-data/src/plugins/types.ts index 8fd50b17b..797c9a53f 100644 --- a/packages/velaux-data/src/plugins/types.ts +++ b/packages/velaux-data/src/plugins/types.ts @@ -61,6 +61,7 @@ export interface PluginMeta extends PluginLink, SourceA // System.load & relative URLS module: string; baseUrl: string; + position?: string; // Define plugin requirements dependencies?: PluginDependencies; diff --git a/packages/velaux-ui/src/components/WorkflowStudio/step-form.tsx b/packages/velaux-ui/src/components/WorkflowStudio/step-form.tsx index 9affcf3bc..6059045cd 100644 --- a/packages/velaux-ui/src/components/WorkflowStudio/step-form.tsx +++ b/packages/velaux-ui/src/components/WorkflowStudio/step-form.tsx @@ -10,13 +10,17 @@ import Group from '../../extends/Group'; import './index.less'; import { StepSelect } from '../../extends/StepSelect'; import i18n from '../../i18n'; -import type { DefinitionDetail , WorkflowStepBase } from '@velaux/data'; +import type { DefinitionDetail, WorkflowStepBase } from '@velaux/data'; import { replaceUrl } from '../../utils/common'; import DrawerWithFooter from '../Drawer'; import { If } from '../If'; import { Translation } from '../Translation'; import UISchema from '../UISchema'; +import { DefinitinPluginRoot } from '../../layout/AppRootPage'; +import { PluginMeta, PluginType } from '@velaux/data'; +import { getPluginSrv } from '../../services/PluginService'; + import { InputItems } from './input-item'; import { OutputItems } from './output-item'; import { BiCodeBlock, BiLaptop } from 'react-icons/bi'; @@ -32,6 +36,7 @@ type State = { definitionDetail?: DefinitionDetail; definitionLoading: boolean; propertiesMode: 'native' | 'code'; + plugin?: PluginMeta | null, }; class StepForm extends Component { @@ -43,6 +48,7 @@ class StepForm extends Component { this.state = { definitionLoading: false, propertiesMode: 'native', + plugin: null, }; this.field = new Field(this, { onChange: (name: string, value: string) => { @@ -61,6 +67,18 @@ class StepForm extends Component { this.field.setValues({ properties: JSON.parse(properties) }); } this.onDetailDefinition(type); + this.loadDefinitionPlugins(type); + }; + + loadDefinitionPlugins = (type: string) => { + return getPluginSrv() + .listAppPagePlugins( PluginType.Definition) + .then((plugins) => { + const plugin = plugins.find(o => o?.position === "definition.workflow_step." + type); + this.setState({ + plugin: plugin || null + }); + }); }; setValues = (values: any | null) => { @@ -110,7 +128,7 @@ class StepForm extends Component { const { Row, Col } = Grid; const FormItem = Form.Item; const { onClose, isSubStep } = this.props; - const { definitionDetail, propertiesMode } = this.state; + const { definitionDetail, propertiesMode, plugin } = this.state; const validator = (rule: Rule, value: any, callback: (error?: string) => void) => { this.uiSchemaRef.current?.validate(callback); }; @@ -119,6 +137,9 @@ class StepForm extends Component { const mode = isSubStep ? workflow.subMode : workflow.mode; const groupStep = this.field.getValue('type') == 'step-group'; + + + return ( {'Edit Step'}} @@ -143,7 +164,7 @@ class StepForm extends Component { required={true} hasToggleIcon={true} > - +
@@ -189,6 +210,19 @@ class StepForm extends Component { + + + diff --git a/packages/velaux-ui/src/layout/AppRootPage/index.tsx b/packages/velaux-ui/src/layout/AppRootPage/index.tsx index 34e557dc0..6127f3c83 100644 --- a/packages/velaux-ui/src/layout/AppRootPage/index.tsx +++ b/packages/velaux-ui/src/layout/AppRootPage/index.tsx @@ -13,9 +13,15 @@ interface Props { dispatch: any; pluginList: PluginMeta[]; loading: any; + project?: string; } -function RootPage({ pluginId }: Props) { +interface DefinitinProps { + pluginId: string; + project?: string; +} + +function RootPage({ pluginId, ...rest }: Props | DefinitinProps, ref: React.ForwardedRef) { const [app, setApp] = React.useState(); React.useEffect(() => { loadAppPlugin(pluginId, setApp); @@ -31,7 +37,7 @@ function RootPage({ pluginId }: Props) { const AppRootPage = app.root return (
- +
); } @@ -272,3 +278,4 @@ const mapStateToProps = (store: any) => ({ export const AppConfigPage = connect(mapStateToProps)(ConfigPage) export const AppRootPage = connect(mapStateToProps)(RootPage) +export const DefinitinPluginRoot = React.forwardRef(RootPage); diff --git a/packages/velaux-ui/src/pages/ApplicationConfig/components/ComponentDialog/index.tsx b/packages/velaux-ui/src/pages/ApplicationConfig/components/ComponentDialog/index.tsx index 471f6690b..4bf4e0542 100644 --- a/packages/velaux-ui/src/pages/ApplicationConfig/components/ComponentDialog/index.tsx +++ b/packages/velaux-ui/src/pages/ApplicationConfig/components/ComponentDialog/index.tsx @@ -26,6 +26,10 @@ import { checkName } from '../../../../utils/common'; import { locale } from '../../../../utils/locale'; import { transComponentDefinitions } from '../../../../utils/utils'; +import { DefinitinPluginRoot } from '../../../../layout/AppRootPage'; +import { PluginMeta, PluginType } from '@velaux/data'; +import { getPluginSrv } from '../../../../services/PluginService'; + import './index.less'; import Permission from '../../../../components/Permission'; @@ -52,6 +56,7 @@ type State = { editComponent?: ApplicationComponent; loading: boolean; propertiesMode: string; + plugins: PluginMeta[]; }; @connect() @@ -66,6 +71,7 @@ class ComponentDialog extends React.Component { isUpdateComponentLoading: false, loading: true, propertiesMode: 'native', + plugins: [] }; this.uiSchemaRef = React.createRef(); } @@ -103,6 +109,16 @@ class ComponentDialog extends React.Component { }); } + loadDefinitionPlugins = () => { + return getPluginSrv() + .listAppPagePlugins(PluginType.Definition) + .then((plugins) => { + this.setState({ + plugins: plugins + }); + }); + }; + onGetEditComponentInfo(callback?: () => void) { const { appName, componentName } = this.props; this.setState({ loading: true }); @@ -312,12 +328,16 @@ class ComponentDialog extends React.Component { const init = this.field.init; const FormItem = Form.Item; const { Row, Col } = Grid; - const { isEditComponent, componentDefinitions, onComponentClose } = this.props; - const { definitionDetail, loading, propertiesMode } = this.state; + const { isEditComponent, componentDefinitions, onComponentClose, project } = this.props; + const { definitionDetail, loading, propertiesMode, plugins } = this.state; const validator = (rule: Rule, value: any, callback: (error?: string) => void) => { this.uiSchemaRef.current?.validate(callback); }; + const componentType: string = this.field.getValue('componentType') || ''; + const plugin = plugins.find(o => o?.position === "definition.component." + componentType); + const usePlugin = componentType && plugin; + return ( { subTitle={ definitionDetail && definitionDetail.uiSchema ? [ - , - ] + , + ] : [] } > - + { mode={isEditComponent ? 'edit' : 'new'} /> + + + diff --git a/packages/velaux-ui/src/pages/ApplicationConfig/components/PolicyDialog/index.tsx b/packages/velaux-ui/src/pages/ApplicationConfig/components/PolicyDialog/index.tsx index c73ffaf5d..86db81285 100644 --- a/packages/velaux-ui/src/pages/ApplicationConfig/components/PolicyDialog/index.tsx +++ b/packages/velaux-ui/src/pages/ApplicationConfig/components/PolicyDialog/index.tsx @@ -23,7 +23,8 @@ import type { EnvBinding, UpdatePolicyRequest, Workflow, - DefinitionBase } from '@velaux/data'; + DefinitionBase +} from '@velaux/data'; import './index.less'; import classNames from 'classnames'; @@ -31,6 +32,9 @@ import classNames from 'classnames'; import { checkName } from '../../../../utils/common'; import { locale } from '../../../../utils/locale'; import UISchema from '../../../../components/UISchema'; +import { PluginMeta, PluginType } from '@velaux/data'; +import { getPluginSrv } from '../../../../services/PluginService'; +import { DefinitinPluginRoot } from '../../../../layout/AppRootPage'; import type { Rule } from '@alifd/next/lib/field'; import { connect } from 'dva'; @@ -47,7 +51,7 @@ type Props = { policy?: ApplicationPolicyDetail; onClose: () => void; onOK: () => void; - dispatch?: ({}) => {}; + dispatch?: ({ }) => {}; }; type PolicyItem = { @@ -67,6 +71,7 @@ type State = { definitionDetailLoading: boolean; definitions?: DefinitionBase[]; propertiesMode: 'native' | 'code'; + plugins: PluginMeta[]; }; @connect() @@ -154,6 +159,7 @@ class PolicyDialog extends React.Component { createPolicyLoading: false, definitionDetailLoading: false, propertiesMode: 'native', + plugins: [] }; this.field = new Field(this, { onChange: (name: string, value: any) => { @@ -168,6 +174,7 @@ class PolicyDialog extends React.Component { componentDidMount() { this.setUISchemaContext(); const { policy } = this.props; + this.loadDefinitionPlugins(); if (policy) { let selected = false; this.state.items.map((item) => { @@ -195,6 +202,16 @@ class PolicyDialog extends React.Component { } } + loadDefinitionPlugins = () => { + return getPluginSrv() + .listAppPagePlugins(PluginType.Definition) + .then((plugins) => { + this.setState({ + plugins: plugins + }); + }); + }; + handleTypeChange = (value: string) => { this.removeProperties(() => { this.loadPolicyDefinitionDetail(value); @@ -460,14 +477,18 @@ class PolicyDialog extends React.Component { }; render() { - const { onClose, policy } = this.props; - const { items, selectedPolicyItem, definitionDetailLoading, policyDefinitionDetail, propertiesMode } = this.state; + const { onClose, policy, project } = this.props; + const { items, selectedPolicyItem, plugins, definitionDetailLoading, policyDefinitionDetail, propertiesMode } = this.state; const validator = (rule: Rule, value: any, callback: (error?: string) => void) => { this.uiSchemaRef.current?.validate(callback); }; const init = this.field.init; const showType = (selectedPolicyItem && selectedPolicyItem?.name == 'custom') || policy != undefined; const span = showType ? 8 : 12; + + const plugin = plugins.find(o => o?.position === "definition.policy." + selectedPolicyItem?.type); + const usePlugin = selectedPolicyItem && plugin; + return ( { } > - + + + + + + diff --git a/packages/velaux-ui/src/pages/ApplicationConfig/components/TraitDialog/index.tsx b/packages/velaux-ui/src/pages/ApplicationConfig/components/TraitDialog/index.tsx index cb19bfd28..0973e92ca 100644 --- a/packages/velaux-ui/src/pages/ApplicationConfig/components/TraitDialog/index.tsx +++ b/packages/velaux-ui/src/pages/ApplicationConfig/components/TraitDialog/index.tsx @@ -12,7 +12,11 @@ import { If } from '../../../../components/If'; import { Translation } from '../../../../components/Translation'; import UISchema from '../../../../components/UISchema'; import i18n from '../../../../i18n'; -import type { ApplicationComponent, DefinitionDetail, Trait , DefinitionBase } from '@velaux/data'; +import type { ApplicationComponent, DefinitionDetail, Trait, DefinitionBase } from '@velaux/data'; + +import { DefinitinPluginRoot } from '../../../../layout/AppRootPage'; +import { PluginMeta, PluginType } from '@velaux/data'; +import { getPluginSrv } from '../../../../services/PluginService'; type Props = { project: string; @@ -38,6 +42,7 @@ type State = { podDisruptive?: any; component?: ApplicationComponent; propertiesMode: 'native' | 'code'; + plugins: PluginMeta[]; }; @connect() class TraitDialog extends React.Component { @@ -50,6 +55,7 @@ class TraitDialog extends React.Component { isLoading: false, traitDefinitions: [], propertiesMode: 'native', + plugins: [] }; this.field = new Field(this); this.uiSchemaRef = React.createRef(); @@ -82,6 +88,16 @@ class TraitDialog extends React.Component { }); } + loadDefinitionPlugins = () => { + return getPluginSrv() + .listAppPagePlugins(PluginType.Definition) + .then((plugins) => { + this.setState({ + plugins: plugins + }); + }); + }; + onGetComponentInfo(callback: () => void) { const { appName, componentName } = this.props; if (appName && componentName) { @@ -273,13 +289,16 @@ class TraitDialog extends React.Component { const init = this.field.init; const FormItem = Form.Item; const { Row, Col } = Grid; - const { onClose, isEditTrait } = this.props; - const { definitionDetail, definitionLoading, podDisruptive, propertiesMode } = this.state; + const { onClose, isEditTrait, project } = this.props; + const { definitionDetail, definitionLoading, podDisruptive, propertiesMode, plugins } = this.state; const validator = (rule: Rule, value: any, callback: (error?: string) => void) => { this.uiSchemaRef.current?.validate(callback); }; - const traitType: string = this.field.getValue('type'); + const traitType: string = this.field.getValue('type'); + const plugin = plugins.find(o => o?.position === "definition.trait." + traitType); + const usePlugin = traitType && plugin; + return ( { - + + + + + + diff --git a/packages/velaux-ui/src/services/MenuService.tsx b/packages/velaux-ui/src/services/MenuService.tsx index 81d75623f..0afdd1f42 100644 --- a/packages/velaux-ui/src/services/MenuService.tsx +++ b/packages/velaux-ui/src/services/MenuService.tsx @@ -15,6 +15,8 @@ import { MdConfirmationNumber } from 'react-icons/md'; import { locationService } from './LocationService'; import { checkPermission } from '../utils/permission'; import { getPluginSrv } from './PluginService'; +import { PluginType } from '@velaux/data'; + const defaultWorkspaces: Workspace[] = [ { @@ -212,7 +214,7 @@ export class MenuWrapper implements MenuService { return Promise.resolve(this.menus); } return getPluginSrv() - .listAppPagePlugins() + .listAppPagePlugins(PluginType.PageApp) .then((plugins) => { plugins.map((plugin) => { plugin.includes?.map((include) => { diff --git a/packages/velaux-ui/src/services/PluginService.ts b/packages/velaux-ui/src/services/PluginService.ts index a1446f70f..8592054ae 100644 --- a/packages/velaux-ui/src/services/PluginService.ts +++ b/packages/velaux-ui/src/services/PluginService.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; // eslint-disable-line lodash/import-scope import moment from 'moment'; // eslint-disable-line no-restricted-imports import react from 'react'; import * as velauxData from '@velaux/data'; // eslint-disable-line no-restricted-imports -import { AppPagePlugin, PluginLink, PluginMeta, PluginType } from '@velaux/data'; +import { AppPagePlugin, PluginMeta, PluginType } from '@velaux/data'; import * as velauxUI from '../types'; // eslint-disable-line no-restricted-imports import * as ReactDom from 'react-dom'; import * as DvaRouter from 'dva/router'; @@ -85,9 +85,9 @@ export function importAppPagePlugin(meta: PluginMeta): Promise { * A wrapper to generate the menu configs */ export interface PluginService { - listAppPagePlugins(): Promise; + listAppPagePlugins(type: PluginType): Promise; - loadMeta(pluginID: string): Promise; + loadMeta(pluginID: string): Promise; } /** @internal */ @@ -95,13 +95,13 @@ export class PluginWrapper implements PluginService { constructor() { } - listAppPagePlugins(): Promise { + listAppPagePlugins(type = PluginType.PageApp): Promise { return getBackendSrv() .get('/api/v1/plugins') .then((res: any) => { if (res) { const plugins = res.plugins ? (res.plugins as PluginMeta[]) : []; - return Promise.resolve(plugins.filter((p) => p.type === PluginType.PageApp)); + return Promise.resolve(plugins.filter((p) => p.type === type)); } return Promise.reject(new Error('Unknown Plugins')); }); diff --git a/pkg/plugin/types/plugin.go b/pkg/plugin/types/plugin.go index 8a346be06..3b91dee5f 100644 --- a/pkg/plugin/types/plugin.go +++ b/pkg/plugin/types/plugin.go @@ -81,6 +81,7 @@ type JSONData struct { // there are four sub types in the definition plugin type, includes: component, trait, policy ,and workflow-step. SubType string `json:"subType"` Name string `json:"name"` + Position string `json:"position"` Info Info `json:"info"` Includes []*Includes `json:"includes"` Category string `json:"category"` diff --git a/pkg/server/interfaces/api/assembler/v1/do2dto.go b/pkg/server/interfaces/api/assembler/v1/do2dto.go index edcc56d9e..3d9671592 100644 --- a/pkg/server/interfaces/api/assembler/v1/do2dto.go +++ b/pkg/server/interfaces/api/assembler/v1/do2dto.go @@ -307,6 +307,7 @@ func PluginToDTO(p pluginTypes.Plugin) apisv1.PluginDTO { return apisv1.PluginDTO{ ID: p.ID, Name: p.Name, + Position: p.Position, Category: p.Category, Type: p.Type, SubType: p.SubType, diff --git a/pkg/server/interfaces/api/dto/v1/types.go b/pkg/server/interfaces/api/dto/v1/types.go index bcd4809e7..1a92d2921 100644 --- a/pkg/server/interfaces/api/dto/v1/types.go +++ b/pkg/server/interfaces/api/dto/v1/types.go @@ -1898,6 +1898,7 @@ type PluginDTO struct { // there are four sub types in the definition plugin type, includes: component, trait, policy ,and workflow-step. SubType string `json:"subType"` Name string `json:"name"` + Position string `json:"position"` Info pluginTypes.Info `json:"info"` Includes []*pluginTypes.Includes `json:"includes"` Category string `json:"category"`