You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2022/10/31 02:57:07 UTC
[incubator-devlake] branch main updated: feat: setup configuration-ui plugin registry (#2886)
This is an automated email from the ASF dual-hosted git repository.
likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new c215d73f feat: setup configuration-ui plugin registry (#2886)
c215d73f is described below
commit c215d73fba30c1c0114ab5be871f15b3645d6a53
Author: Julien Chinapen <ju...@merico.dev>
AuthorDate: Sun Oct 30 22:57:02 2022 -0400
feat: setup configuration-ui plugin registry (#2886)
* feat: enable rate limit per hour toggle options
* fix: reset rateLimit to 0 on disable
* fix: add effect dependency
* fix: update rate limit config for new providers
* fix: extract rate limit tooltip as data const
* fix: use shared rate limit tooltip for bitbucket
* feat: setup configuration-ui plugin registry
* fix: update configure connection with new hook
* fix: register auzre bitbucket and gitee plugins
* fix: register dbt dora refdiff and starrocks
* feat: create hoc for loading transformations
* fix: fetch all connection sources dynamically
* fix: register gitextractor plugin
* fix: register feishu plugin
* fix: add exist check for transform components
* fix: build sidebar connections menu dynamically
* fix: memoize sidebar active menu
* chore: cleanup unused code in connection dialog
* chore: disable legacy blueprint add dialog
* fix: use dynamic providers on data scopes grid
* fix: use provider configs from integrations hook
* fix: create menu item data model
* fix: use provider labels config from igm hook
* fix: only register plugins that are enabled
* fix: setup tooltips from plugin registry
* fix: import tooltips on create-blueprint
* docs: update readme.md for config-ui
* chore: cleanup provider transformation settings
* fix: restore connection limit configuration
* fix: add basic plugin validation
* fix: create connection status data constant
* chore: update comments in Providers.js
* feat: create integrations context and provider
* fix: use context for connections selector
* fix: use integrations context for bp validation
* refactor: use integrations context with hooks
* refactor: use integrations context on DataScopes
* fix: use integrations context on ConnectionForm
* refactor: use integrations context on stage task
* fix: add provider labels to integrations context
* fix: add private prop to Plugin data model
* refactor: store default transforms in registry
* fix: replace props with integrations context
* fix: cleanup json formatting for registry files
* refactor: build scope options from registry
* fix: update plugin model with fields getter
* fix: generate key from entity model
* fix: get default entities from plugin instance
* fix: remove unused Providers in useJIRA hook
* fix: remove unused integrations hook props
* fix: remove unused integrations hook props
* fix: use integrations context with Deployment.jsx
* fix: use double bang syntax for connection limit
---
config-ui/README.md | 126 +++++++++
config-ui/src/components/NoData.jsx | 4 +-
config-ui/src/components/Sidebar.jsx | 59 ++--
.../src/components/Sidebar/MenuConfiguration.jsx | 128 +++------
.../src/components/blueprints/ConnectionDialog.jsx | 86 +-----
.../components/blueprints/ConnectionsSelector.jsx | 39 +--
.../src/components/blueprints/DataScopesGrid.jsx | 3 +-
.../blueprints/ProviderTransformationSettings.jsx | 150 ++++------
.../blueprints/create-workflow/DataScopes.jsx | 7 +-
.../blueprints/create-workflow/DataSync.jsx | 14 +-
.../create-workflow/DataTransformations.jsx | 29 +-
.../blueprints/transformations/CICD/Deployment.jsx | 16 +-
.../src/components/pipelines/StageTaskCaption.jsx | 2 +-
.../src/components/pipelines/StageTaskName.jsx | 7 +-
.../src/{index.js => data/ConnectionStatus.js} | 23 +-
config-ui/src/data/NullProvider.js | 1 +
config-ui/src/data/Providers.js | 24 +-
config-ui/src/data/integrations.jsx | 2 +
.../hooks/data-scope/useTransformationsManager.jsx | 221 ++++++++-------
config-ui/src/hooks/useBlueprintValidation.jsx | 7 +-
config-ui/src/hooks/useConnectionManager.jsx | 52 ++--
config-ui/src/hooks/useConnectionValidation.jsx | 8 +-
config-ui/src/hooks/useDataScopesManager.jsx | 209 ++++++++------
config-ui/src/hooks/useIntegrations.jsx | 305 ++++++++++++++++++++-
config-ui/src/hooks/useJIRA.jsx | 19 --
config-ui/src/hooks/usePipelineManager.jsx | 21 +-
config-ui/src/hooks/usePipelineValidation.jsx | 22 +-
config-ui/src/index.js | 5 +-
config-ui/src/models/DataEntity.js | 102 +++++++
config-ui/src/models/GithubProject.js | 7 +
config-ui/src/models/GitlabProject.js | 7 +
config-ui/src/models/JenkinsJob.js | 6 +
config-ui/src/models/JiraBoard.js | 7 +
.../src/models/{JenkinsJob.js => MenuItem.js} | 41 ++-
config-ui/src/models/Plugin.js | 109 ++++++++
.../src/pages/blueprints/blueprint-detail.jsx | 19 +-
.../src/pages/blueprints/blueprint-settings.jsx | 44 ++-
.../src/pages/blueprints/create-blueprint.jsx | 131 ++++-----
config-ui/src/pages/blueprints/index.jsx | 67 +++--
.../pages/configure/connections/AddConnection.jsx | 111 ++++----
.../configure/connections/ConfigureConnection.jsx | 153 ++++++-----
.../pages/configure/connections/ConnectionForm.jsx | 72 ++++-
.../src/pages/configure/integration/index.jsx | 68 ++++-
.../src/pages/configure/integration/manage.jsx | 61 +++--
config-ui/src/pages/configure/settings/github.jsx | 2 +
config-ui/src/pages/configure/settings/jira.jsx | 2 +
config-ui/src/pages/configure/settings/tapd.jsx | 2 +
config-ui/src/registry/plugins/ae.json | 13 +
config-ui/src/registry/plugins/azure.json | 53 ++++
config-ui/src/registry/plugins/bitbucket.json | 53 ++++
config-ui/src/registry/plugins/dbt.json | 12 +
config-ui/src/registry/plugins/dora.json | 12 +
config-ui/src/registry/plugins/feishu.json | 12 +
config-ui/src/registry/plugins/gitee.json | 56 ++++
config-ui/src/registry/plugins/gitextractor.json | 12 +
config-ui/src/registry/plugins/github.json | 63 +++++
config-ui/src/registry/plugins/gitlab.json | 52 ++++
config-ui/src/registry/plugins/jenkins.json | 54 ++++
config-ui/src/registry/plugins/jira.json | 65 +++++
config-ui/src/registry/plugins/refdiff.json | 12 +
config-ui/src/registry/plugins/starrocks.json | 12 +
config-ui/src/registry/plugins/tapd.json | 55 ++++
config-ui/src/store/integrations-context.jsx | 73 +++++
63 files changed, 2296 insertions(+), 913 deletions(-)
diff --git a/config-ui/README.md b/config-ui/README.md
index d72cf86d..09aa0bc6 100644
--- a/config-ui/README.md
+++ b/config-ui/README.md
@@ -42,6 +42,132 @@ Server will listen on `http://localhost:9000`
For actual production use, the **Docker Image** for Config-UI should be used as outlined in the main project README.md
+## Plugin Registration
+
+### Step 1. Create Registry JSON Configuration
+Depending on the nature of the plugin, see a similar integration in `registry/plugins/` folder and copy as base template. For example, to model after GitHub copy `registry/plugins/github.json` to a uniquely named JSON registry file.
+
+```
+# Example "Merico"
+$> cp registry/plugins/github.json registry/plugins/merico.json"
+```
+
+**Configure Plugin Options**
+The newly created registry file needs to customized with **Name** and **Type** (`Default="integration"`), and all related **Connection** property fields (_Labels_, Tooltips, etc)
+
+Since not all Plugins created may have **UI** capabilities, the `type` property is used to distinguish _variance_ in behavior.
+
+- integration
+- plugin
+- pipeline
+- webhook
+
+The **Enabled** property (`enabled`) must be set to `true` to allow the plugin the be registered.
+
+```
+# Sample Config for "Merico" Plugin
+{
+ "id": "merico",
+ "name": "Merico",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/merico.svg",
+ ...
+ ...
+ ...
+}
+```
+
+### Step 2. Register the JSON Configuration
+Next, the new plugin needs to be registered with the **Integrations Manager Hook** (`@hooks/useIntegrations.jsx`).
+1. Import the new Plugin JSON File
+2. Add Plugin to the bottom of `pluginRegistry` Array
+
+```
+$> vi hooks/useIntegrations.jsx`
+....
+....
+import DbtPlugin from '@/registry/plugins/dbt.json'
+import StarrocksPlugin from '@/registry/plugins/starrocks.json'
+import DoraPlugin from '@/registry/plugins/dora.json'
+# Register Merico Plugin
++ import MericoPlugin from '@/registry/plugins/merico.json'
+
+function useIntegrations(
+ pluginRegistry = [
+ JiraPlugin,
+ GitHubPlugin,
+ ...
+ ...
+ # Load Merico Plugin
+ + MericoPlugin
+
+```
+
+### Step 3. Define & Register Transformation Settings Component (Optional)
+
+If this new Plugin requires **Transformation Settings**, the transformation settings component must be created and imported. You may use an existing transformation settings file as a reference. In the future there will be support to create transformation settings dynamically from configuration.
+
+1. Create and Design React Transformation **JSX Component** in `settings/[my-plugin.jsx]`
+
+```jsx
+# Example Transformation Settings Template for "Merico"
+export default function MericoSettings(props) {
+ const {
+ Providers,
+ ProviderLabels,
+ provider,
+ connection,
+ entities = [],
+ transformation = {},
+ isSaving = false,
+ isSavingConnection = false,
+ onSettingsChange = () => {}
+ } = props
+
+ return (
+ <>
+
+ </>
+ )
+}
+```
+
+3. Import & **Register Provider Transformation Settings** in `components/blueprints/ProviderTransformationSettings.jsx`. Update the `TransformationComponents` Map to Load the Plugin
+
+```jsx
+# components/blueprints/ProviderTransformationSettings.jsx
+import AzureSettings from '@/pages/configure/settings/azure'
+import BitbucketSettings from '@/pages/configure/settings/bitbucket'
+import GiteeSettings from '@/pages/configure/settings/gitee'
+# Import/Register Merico Plugin
++ import MericoSettings from '@/pages/configure/settings/merico'
+
+const ProviderTransformationSettings = (props) => {
+ ...
+ ...
+ ...
+ // Provider Transformation Components (LOCAL)
+ const TransformationComponents = useMemo(
+ () => ({
+ ...
+ ...
+ [Providers.AZURE]: AzureSettings,
+ [Providers.BITBUCKET]: BitbucketSettings,
+ [Providers.GITEE]: GiteeSettings
+ # Load Merico Plugin
+ + [Providers.MERICO]: MericoSettings
+ }),
+ [Providers]
+ )
+
+}
+```
+
+
## Testing
### Cypress E2E Tests
diff --git a/config-ui/src/components/NoData.jsx b/config-ui/src/components/NoData.jsx
index a3fb1f48..b63c5d1f 100644
--- a/config-ui/src/components/NoData.jsx
+++ b/config-ui/src/components/NoData.jsx
@@ -40,10 +40,12 @@ const NoData = (props) => {
</h4>
<div>{message}</div>
</div>
- {onClick && actionText && (
+ {onClick && actionText ? (
<Button intent={Intent.NONE} onClick={onClick}>
{actionText}
</Button>
+ ) : (
+ <p> </p>
)}
</div>
</>
diff --git a/config-ui/src/components/Sidebar.jsx b/config-ui/src/components/Sidebar.jsx
index 3c3356da..5714353e 100644
--- a/config-ui/src/components/Sidebar.jsx
+++ b/config-ui/src/components/Sidebar.jsx
@@ -15,7 +15,13 @@
* limitations under the License.
*
*/
-import React, { useEffect, useState, useContext } from 'react'
+import React, {
+ useEffect,
+ useState,
+ useContext,
+ useMemo,
+ useCallback
+} from 'react'
import {
// BrowserRouter as Router,
useRouteMatch
@@ -29,35 +35,46 @@ import { ReactComponent as Logo } from '@/images/devlake-logo.svg'
import { ReactComponent as LogoText } from '@/images/devlake-textmark.svg'
import '@/styles/sidebar.scss'
-import UIContext from '../store/UIContext'
+import UIContext from '@/store/UIContext'
-const Sidebar = () => {
+const Sidebar = (props) => {
+ const { integrations = [] } = props
const activeRoute = useRouteMatch()
const uiContext = useContext(UIContext)
- const [menu, setMenu] = useState(MenuConfiguration(activeRoute))
+ const getMenu = useCallback(
+ () => MenuConfiguration(activeRoute, integrations),
+ [activeRoute, integrations]
+ )
+
+ const ActiveMenu = useMemo(() => getMenu(), [getMenu])
+
+ const [menu, setMenu] = useState(ActiveMenu)
const [versionTag, setVersionTag] = useState('')
- useEffect(() => {
- setMenu(MenuConfiguration(activeRoute))
- }, [activeRoute])
+ // useEffect(() => {
+ // setMenu(ActiveMenu)
+ // }, [ActiveMenu])
useEffect(() => {
- const fetchVersion = async () => {
- try {
- const versionUrl = `${DEVLAKE_ENDPOINT}/version`
- const res = await request.get(versionUrl).catch((e) => {
- console.log('>>> API VERSION ERROR...', e)
- setVersionTag('')
- })
- setVersionTag(res?.data ? res.data?.version : '')
- } catch (e) {
- setVersionTag('')
- }
- }
- fetchVersion()
+ // @todo: re-enable version fetch
+ // const fetchVersion = async () => {
+ // try {
+ // const versionUrl = `${DEVLAKE_ENDPOINT}/version`
+ // const res = await request.get(versionUrl).catch((e) => {
+ // console.log('>>> API VERSION ERROR...', e)
+ // setVersionTag('')
+ // })
+ // setVersionTag(res?.data ? res.data?.version : '')
+ // } catch (e) {
+ // setVersionTag('')
+ // }
+ // }
+ // fetchVersion()
}, [])
+ // useEffect(() => {}, [integrations])
+
return uiContext.sidebarVisible ? (
<Card
interactive={false}
@@ -85,7 +102,7 @@ const Sidebar = () => {
>
<sup style={{ fontSize: '9px', color: '#cccccc', marginLeft: '-30px' }}>DEV</sup>LAKE
</h3> */}
- <SidebarMenu menu={menu} />
+ <SidebarMenu menu={ActiveMenu} />
<span className='copyright-tag'>
{/* <span className='version-tag'>{versionTag || ''}</span><br /> */}
<strong>Apache 2.0 License</strong>
diff --git a/config-ui/src/components/Sidebar/MenuConfiguration.jsx b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
index 8b674201..ce51c3a3 100644
--- a/config-ui/src/components/Sidebar/MenuConfiguration.jsx
+++ b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
@@ -16,10 +16,11 @@
*
*/
import React from 'react'
-import { ProviderLabels } from '@/data/Providers'
+// import { ProviderLabels } from '@/data/Providers'
import { GRAFANA_URL } from '@/utils/config'
+import MenuItem from '@/models/MenuItem'
-const MenuConfiguration = (activeRoute) => {
+const MenuConfiguration = (activeRoute, integrations = []) => {
return [
{
id: 0,
@@ -29,98 +30,37 @@ const MenuConfiguration = (activeRoute) => {
activeRoute.url.startsWith('/integrations') || activeRoute.url === '/',
icon: 'data-connection',
classNames: [],
- children: [
- {
- id: 0,
- label: ProviderLabels.JIRA,
- route: '/integrations/jira',
- active:
- activeRoute.url.endsWith('/integrations/jira') ||
- activeRoute.url.endsWith('/jira'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 1,
- label: ProviderLabels.GITHUB,
- route: '/integrations/github',
- active:
- activeRoute.url.endsWith('/integrations/github') ||
- activeRoute.url.endsWith('/github'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 2,
- label: ProviderLabels.GITLAB,
- route: '/integrations/gitlab',
- active:
- activeRoute.url.endsWith('/integrations/gitlab') ||
- activeRoute.url.endsWith('/gitlab'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 3,
- label: ProviderLabels.JENKINS,
- route: '/integrations/jenkins',
- active:
- activeRoute.url.endsWith('/integrations/jenkins') ||
- activeRoute.url.endsWith('/jenkins'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 4,
- label: `${ProviderLabels.TAPD} (beta)`,
- route: '/integrations/tapd',
- active:
- activeRoute.url.endsWith('/integrations/tapd') ||
- activeRoute.url.endsWith('/tapd'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 5,
- label: `${ProviderLabels.AZURE}`,
- route: '/integrations/azure',
- active:
- activeRoute.url.endsWith('/integrations/azure') ||
- activeRoute.url.endsWith('/azure'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 6,
- label: `${ProviderLabels.BITBUCKET}`,
- route: '/integrations/bitbucket',
- active:
- activeRoute.url.endsWith('/integrations/bitbucket') ||
- activeRoute.url.endsWith('/bitbucket'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 7,
- label: `${ProviderLabels.GITEE}`,
- route: '/integrations/gitee',
- active:
- activeRoute.url.endsWith('/integrations/gitee') ||
- activeRoute.url.endsWith('/gitee'),
- icon: 'layers',
- classNames: []
- },
- {
- id: 8,
- label: 'Incoming Webhook',
- route: '/connections/incoming-webhook',
- active:
- activeRoute.url.endsWith('/connections/incoming-webhook') ||
- activeRoute.url.endsWith('/webhook'),
- icon: 'layers',
- classNames: []
- }
- ]
+ children: integrations.map(
+ (p, pIdx) =>
+ new MenuItem({
+ id: pIdx,
+ // @todo: support disable prop with json config
+ // disabled: false,
+ label: p?.name,
+ route: `/integrations/${p?.id}`,
+ active:
+ activeRoute.url.endsWith(`/integrations/${p?.id}`) ||
+ activeRoute.url.endsWith(`${p.id}`),
+ icon: p?.icon ? (
+ <img
+ className='providerMenuIconSvg'
+ src={'/' + p?.icon}
+ width={16}
+ height={16}
+ style={{
+ display: 'flex',
+ alignSelf: 'center',
+ width: '16px',
+ height: '16px'
+ }}
+ />
+ ) : (
+ 'layers'
+ ),
+ classNames: []
+ // children: []
+ })
+ )
},
{
id: 1,
diff --git a/config-ui/src/components/blueprints/ConnectionDialog.jsx b/config-ui/src/components/blueprints/ConnectionDialog.jsx
index aa34454f..b0d50f3a 100644
--- a/config-ui/src/components/blueprints/ConnectionDialog.jsx
+++ b/config-ui/src/components/blueprints/ConnectionDialog.jsx
@@ -30,15 +30,6 @@ import {
MenuItem
} from '@blueprintjs/core'
import { Select } from '@blueprintjs/select'
-import {
- Providers,
- // ProviderTypes,
- ProviderLabels,
- ProviderFormLabels,
- ProviderFormPlaceholders,
- ProviderConnectionLimits
- // ProviderIcons,
-} from '@/data/Providers'
import { NullBlueprintConnection } from '@/data/NullBlueprintConnection'
import InputValidationError from '@/components/validation/InputValidationError'
import ContentLoader from '@/components/loaders/ContentLoader'
@@ -49,58 +40,6 @@ const Modes = {
EDIT: 'edit'
}
-// @todo: lift data sources list to configuration level, requires expansion when more providers are added..
-const DATA_SOURCES_LIST = [
- {
- id: 1,
- name: Providers.JIRA,
- title: ProviderLabels[Providers.JIRA.toUpperCase()],
- value: Providers.JIRA
- },
- {
- id: 2,
- name: Providers.GITHUB,
- title: ProviderLabels[Providers.GITHUB.toUpperCase()],
- value: Providers.GITHUB
- },
- {
- id: 3,
- name: Providers.GITLAB,
- title: ProviderLabels[Providers.GITLAB.toUpperCase()],
- value: Providers.GITLAB
- },
- {
- id: 4,
- name: Providers.JENKINS,
- title: ProviderLabels[Providers.JENKINS.toUpperCase()],
- value: Providers.JENKINS
- },
- {
- id: 5,
- name: Providers.TAPD,
- title: ProviderLabels[Providers.TAPD.toUpperCase()],
- value: Providers.TAPD
- },
- {
- id: 6,
- name: Providers.AZURE,
- title: ProviderLabels[Providers.AZURE.toUpperCase()],
- value: Providers.AZURE
- },
- {
- id: 7,
- name: Providers.BITBUCKET,
- title: ProviderLabels[Providers.BITBUCKET.toUpperCase()],
- value: Providers.BITBUCKET
- },
- {
- id: 8,
- name: Providers.GITEE,
- title: ProviderLabels[Providers.GITEE.toUpperCase()],
- value: Providers.GITEE
- }
-]
-
const ConnectionDialog = (props) => {
const {
isOpen = false,
@@ -124,9 +63,11 @@ const ConnectionDialog = (props) => {
isSaving = false,
isValid = false,
// editMode = false,
- dataSourcesList = DATA_SOURCES_LIST,
- labels = ProviderLabels[connection.provider],
- placeholders = ProviderFormPlaceholders[connection.provider],
+ dataSourcesList = [],
+ labels,
+ placeholders,
+ tooltips,
+ sourceLimits,
onTest = () => {},
onSave = () => {},
onClose = () => {},
@@ -242,7 +183,7 @@ const ConnectionDialog = (props) => {
contentClassName='formGroupContent'
>
<Label style={{ display: 'inline', marginRight: 0 }}>
- {labels ? labels.datasource : <>Data Source</>}
+ <>Data Source</>
<span className='requiredStar'>*</span>
</Label>
<Select
@@ -331,17 +272,12 @@ const ConnectionDialog = (props) => {
allTestResponses={allTestResponses}
errors={errors}
showError={showConnectionError}
- authType={
- [Providers.JENKINS, Providers.JIRA].includes(
- activeProvider?.id
- )
- ? 'plain'
- : 'token'
- }
+ authType={activeProvider?.getAuthenticationType()}
showLimitWarning={false}
- sourceLimits={ProviderConnectionLimits}
- labels={ProviderFormLabels[activeProvider?.id]}
- placeholders={ProviderFormPlaceholders[activeProvider?.id]}
+ sourceLimits={sourceLimits}
+ labels={labels}
+ placeholders={placeholders}
+ tooltips={tooltips}
enableActions={false}
// formGroupClassName='formGroup-inline'
showHeadline={false}
diff --git a/config-ui/src/components/blueprints/ConnectionsSelector.jsx b/config-ui/src/components/blueprints/ConnectionsSelector.jsx
index f6438b68..f0f68e80 100644
--- a/config-ui/src/components/blueprints/ConnectionsSelector.jsx
+++ b/config-ui/src/components/blueprints/ConnectionsSelector.jsx
@@ -18,7 +18,8 @@
import React from 'react'
import { Button, Intent, MenuItem } from '@blueprintjs/core'
import { MultiSelect } from '@blueprintjs/select'
-import { ProviderIcons } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { ProviderIcons } from '@/data/Providers'
const ConnectionsSelector = (props) => {
const {
@@ -41,22 +42,26 @@ const ConnectionsSelector = (props) => {
}
key={item.id}
label={
- <span style={{ marginLeft: '20px' }}>
- <span
- style={{
- display: 'inline-block',
- marginTop: '2px',
- width: '14px',
- height: '14px'
- }}
- >
- {ProviderIcons[item.provider] ? (
- ProviderIcons[item.provider](14, 14)
- ) : (
- <></>
- )}
- </span>
- </span>
+ <IntegrationsContext.Consumer>
+ {({ ProviderIcons }) => (
+ <span style={{ marginLeft: '20px' }}>
+ <span
+ style={{
+ display: 'inline-block',
+ marginTop: '2px',
+ width: '14px',
+ height: '14px'
+ }}
+ >
+ {ProviderIcons[item.provider] ? (
+ ProviderIcons[item.provider](14, 14)
+ ) : (
+ <></>
+ )}
+ </span>
+ </span>
+ )}
+ </IntegrationsContext.Consumer>
}
onClick={handleClick}
text={
diff --git a/config-ui/src/components/blueprints/DataScopesGrid.jsx b/config-ui/src/components/blueprints/DataScopesGrid.jsx
index 16c87f5b..c9caac27 100644
--- a/config-ui/src/components/blueprints/DataScopesGrid.jsx
+++ b/config-ui/src/components/blueprints/DataScopesGrid.jsx
@@ -17,11 +17,12 @@
*/
import React from 'react'
import { Button, Intent, Card, Elevation, Tag } from '@blueprintjs/core'
-import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
+// import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
import { NullBlueprint, BlueprintMode } from '@/data/NullBlueprint'
const DataScopesGrid = (props) => {
const {
+ providers: Providers = {},
connections = [],
blueprint = NullBlueprint,
mode = BlueprintMode.NORMAL,
diff --git a/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx b/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx
index f746934d..70b35bac 100644
--- a/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx
+++ b/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx
@@ -15,15 +15,9 @@
* limitations under the License.
*
*/
-import React, { useEffect } from 'react'
-import {
- Providers
- // ProviderTypes,
- // ProviderIcons,
- // ConnectionStatus,
- // ConnectionStatusLabels,
-} from '@/data/Providers'
-// import { DataEntities, DataEntityTypes } from '@/data/DataEntities'
+import React, { useMemo } from 'react'
+import NoData from '@/components/NoData'
+
import JiraSettings from '@/pages/configure/settings/jira'
import GitlabSettings from '@/pages/configure/settings/gitlab'
import JenkinsSettings from '@/pages/configure/settings/jenkins'
@@ -33,8 +27,28 @@ import AzureSettings from '@/pages/configure/settings/azure'
import BitbucketSettings from '@/pages/configure/settings/bitbucket'
import GiteeSettings from '@/pages/configure/settings/gitee'
+// Transformation Higher-Order Component (HOC) Settings Loader
+const withTransformationSettings = (
+ TransformationComponent,
+ TransformationProps
+) =>
+ TransformationComponent ? (
+ <TransformationComponent {...TransformationProps} />
+ ) : (
+ <NoData
+ title='No Transformations'
+ icon='disable'
+ message='This provider does not have additional transformation settings'
+ onClick={null}
+ actionText={null}
+ />
+ )
+
const ProviderTransformationSettings = (props) => {
const {
+ Providers = {},
+ ProviderLabels = {},
+ ProviderIcons = {},
provider,
blueprint,
connection,
@@ -50,102 +64,32 @@ const ProviderTransformationSettings = (props) => {
isFetchingJIRA = false
} = props
- return (
- <div className='transformation-settings' data-provider={provider?.id}>
- {provider?.id === Providers.GITHUB && (
- <GithubSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
-
- {provider?.id === Providers.GITLAB && (
- <GitlabSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
+ // Provider Transformation Components (LOCAL)
+ const TransformationComponents = useMemo(
+ () => ({
+ [Providers.GITHUB]: GithubSettings,
+ [Providers.GITLAB]: GitlabSettings,
+ [Providers.JIRA]: JiraSettings,
+ [Providers.JENKINS]: JenkinsSettings,
+ [Providers.TAPD]: TapdSettings,
+ [Providers.AZURE]: AzureSettings,
+ [Providers.BITBUCKET]: BitbucketSettings,
+ [Providers.GITEE]: GiteeSettings
+ }),
+ [Providers]
+ )
- {provider?.id === Providers.JIRA && (
- <JiraSettings
- provider={provider}
- connection={connection}
- issueTypes={issueTypes}
- fields={fields}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- jiraProxyError={jiraProxyError}
- isFetchingJIRA={isFetchingJIRA}
- />
- )}
+ // Dynamic Transformation Settings via HOC
+ const TransformationWithProviderSettings = withTransformationSettings(
+ provider?.id && TransformationComponents[provider?.id]
+ ? TransformationComponents[provider?.id]
+ : null,
+ { ...props, entities: props.entities[props?.connection?.id] }
+ )
- {provider?.id === Providers.JENKINS && (
- <JenkinsSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
- {provider?.id === Providers.TAPD && (
- <TapdSettings
- provider={provider}
- connection={connection}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
- {provider?.id === Providers.AZURE && (
- <AzureSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
- {provider?.id === Providers.BITBUCKET && (
- <BitbucketSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
- {provider?.id === Providers.GITEE && (
- <GiteeSettings
- provider={provider}
- connection={connection}
- transformation={transformation}
- onSettingsChange={onSettingsChange}
- entities={entities[connection?.id]}
- isSaving={isSaving}
- isSavingConnection={isSavingConnection}
- />
- )}
+ return (
+ <div className='transformation-settings' data-provider={provider?.id}>
+ {TransformationWithProviderSettings}
</div>
)
}
diff --git a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
index 65cb5be4..5262e941 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
@@ -15,7 +15,7 @@
* limitations under the License.
*
*/
-import React, { useEffect, useMemo } from 'react'
+import React, { useEffect, useMemo, useContext } from 'react'
import {
Button,
Card,
@@ -24,7 +24,8 @@ import {
Intent,
TagInput
} from '@blueprintjs/core'
-import { ProviderIcons, Providers } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { ProviderIcons, Providers } from '@/data/Providers'
import ConnectionTabs from '@/components/blueprints/ConnectionTabs'
import BoardsSelector from '@/components/blueprints/BoardsSelector'
import DataEntitiesSelector from '@/components/blueprints/DataEntitiesSelector'
@@ -67,6 +68,8 @@ const DataScopes = (props) => {
cardStyle = {}
} = props
+ const { Providers, ProviderIcons } = useContext(IntegrationsContext)
+
const selectedBoards = useMemo(
() => boards[configuredConnection.id],
[boards, configuredConnection?.id]
diff --git a/config-ui/src/components/blueprints/create-workflow/DataSync.jsx b/config-ui/src/components/blueprints/create-workflow/DataSync.jsx
index 488ce0ca..cce1a429 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataSync.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataSync.jsx
@@ -33,13 +33,13 @@ import {
Card,
Colors
} from '@blueprintjs/core'
-import {
- Providers,
- ProviderTypes,
- ProviderIcons,
- ConnectionStatus,
- ConnectionStatusLabels
-} from '@/data/Providers'
+// import {
+// Providers,
+// ProviderTypes,
+// ProviderIcons,
+// ConnectionStatus,
+// ConnectionStatusLabels
+// } from '@/data/Providers'
import InputValidationError from '@/components/validation/InputValidationError'
diff --git a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
index 9e51e58b..d54c05ff 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
@@ -15,7 +15,13 @@
* limitations under the License.
*
*/
-import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+ useContext
+} from 'react'
import {
Button,
Card,
@@ -26,8 +32,9 @@ import {
MenuItem
} from '@blueprintjs/core'
import { Select } from '@blueprintjs/select'
-import { integrationsData } from '@/data/integrations'
-import { ProviderIcons, Providers } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { integrationsData } from '@/data/integrations'
+// import { ProviderIcons, Providers } from '@/data/Providers'
import { DataEntityTypes } from '@/data/DataEntities'
import { DEFAULT_DATA_ENTITIES } from '@/data/BlueprintWorkflow'
import { Variants } from '@/data/Variants'
@@ -79,6 +86,9 @@ const DataTransformations = (props) => {
cardStyle = {}
} = props
+ const { Integrations, Providers, ProviderIcons, ProviderLabels } =
+ useContext(IntegrationsContext)
+
const noTransformationsAvailable = useMemo(
() =>
[Providers.TAPD].includes(configuredConnection?.provider) ||
@@ -86,7 +96,13 @@ const DataTransformations = (props) => {
dataEntities[configuredConnection?.id].every(
(e) => e.value !== DataEntityTypes.DEVOPS
)),
- [configuredConnection?.provider, configuredConnection?.id, dataEntities]
+ [
+ configuredConnection?.provider,
+ configuredConnection?.id,
+ dataEntities,
+ Providers.TAPD,
+ Providers.GITLAB
+ ]
)
const boardsAndProjects = useMemo(
@@ -366,7 +382,10 @@ const DataTransformations = (props) => {
) && (
<ProviderTransformationSettings
key={configuredProject?.id || configuredBoard?.id}
- provider={integrationsData.find(
+ Providers={Providers}
+ ProviderLabels={ProviderLabels}
+ ProviderIcons={ProviderIcons}
+ provider={Integrations.find(
(i) => i.id === configuredConnection?.provider
)}
blueprint={blueprint}
diff --git a/config-ui/src/components/blueprints/transformations/CICD/Deployment.jsx b/config-ui/src/components/blueprints/transformations/CICD/Deployment.jsx
index 15b46884..5bb2ec70 100644
--- a/config-ui/src/components/blueprints/transformations/CICD/Deployment.jsx
+++ b/config-ui/src/components/blueprints/transformations/CICD/Deployment.jsx
@@ -15,7 +15,8 @@
* limitations under the License.
*
*/
-import React, { useState, useEffect, useMemo } from 'react'
+import React, { useState, useEffect, useMemo, useContext } from 'react'
+import IntegrationsContext from '@/store/integrations-context'
import {
Intent,
FormGroup,
@@ -24,7 +25,6 @@ import {
Radio,
Tag
} from '@blueprintjs/core'
-import { Providers, ProviderLabels } from '@/data/Providers'
const Deployment = (props) => {
const {
@@ -34,6 +34,8 @@ const Deployment = (props) => {
onSettingsChange = () => {}
} = props
+ const { Providers, ProviderLabels } = useContext(IntegrationsContext)
+
const [selectValue, setSelectValue] = useState(1)
useEffect(() => {
@@ -82,7 +84,13 @@ const Deployment = (props) => {
}
return [radio1, radio2]
- }, [provider])
+ }, [
+ provider,
+ ProviderLabels,
+ Providers.JENKINS,
+ Providers.GITHUB,
+ Providers.GITLAB
+ ])
const tagHints = useMemo(() => {
let hint1
@@ -115,7 +123,7 @@ const Deployment = (props) => {
}
return [hint1, hint2]
- }, [provider])
+ }, [provider, Providers.JENKINS, Providers.GITHUB, Providers.GITLAB])
return (
<>
diff --git a/config-ui/src/components/pipelines/StageTaskCaption.jsx b/config-ui/src/components/pipelines/StageTaskCaption.jsx
index c1b5fae9..170ca22c 100644
--- a/config-ui/src/components/pipelines/StageTaskCaption.jsx
+++ b/config-ui/src/components/pipelines/StageTaskCaption.jsx
@@ -18,7 +18,7 @@
import React from 'react'
import { Colors } from '@blueprintjs/core'
import dayjs from '@/utils/time'
-import { Providers } from '@/data/Providers'
+// import { Providers } from '@/data/Providers'
const StageTaskCaption = (props) => {
const { task, options } = props
diff --git a/config-ui/src/components/pipelines/StageTaskName.jsx b/config-ui/src/components/pipelines/StageTaskName.jsx
index 8ad33d18..b4b044ce 100644
--- a/config-ui/src/components/pipelines/StageTaskName.jsx
+++ b/config-ui/src/components/pipelines/StageTaskName.jsx
@@ -15,8 +15,9 @@
* limitations under the License.
*
*/
-import React, { useEffect, useRef } from 'react'
-import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
+import React, { useEffect, useRef, useContext } from 'react'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
import {
Icon,
Colors,
@@ -31,6 +32,8 @@ import dayjs from '@/utils/time'
const StageTaskName = (props) => {
const { task, showDetails = null, onClose = () => {} } = props
+ const { Providers, ProviderIcons, ProviderLabels } =
+ useContext(IntegrationsContext)
const popoverTriggerRef = useRef()
diff --git a/config-ui/src/index.js b/config-ui/src/data/ConnectionStatus.js
similarity index 69%
copy from config-ui/src/index.js
copy to config-ui/src/data/ConnectionStatus.js
index 0a1693a5..a8e001d2 100644
--- a/config-ui/src/index.js
+++ b/config-ui/src/data/ConnectionStatus.js
@@ -15,15 +15,18 @@
* limitations under the License.
*
*/
+const ConnectionStatus = {
+ OFFLINE: 0,
+ ONLINE: 1,
+ DISCONNECTED: 2,
+ TESTING: 3
+}
-import React from 'react'
-import ReactDOM from 'react-dom'
-import App from './App'
-import { UIContextProvider } from '@/store/UIContext'
+const ConnectionStatusLabels = {
+ [ConnectionStatus.OFFLINE]: 'Offline',
+ [ConnectionStatus.ONLINE]: 'Online',
+ [ConnectionStatus.DISCONNECTED]: 'Disconnected',
+ [ConnectionStatus.TESTING]: 'Testing'
+}
-ReactDOM.render(
- <UIContextProvider>
- <App />
- </UIContextProvider>,
- document.getElementById('app')
-)
+export { ConnectionStatus, ConnectionStatusLabels }
diff --git a/config-ui/src/data/NullProvider.js b/config-ui/src/data/NullProvider.js
index 0c20822a..728b9291 100644
--- a/config-ui/src/data/NullProvider.js
+++ b/config-ui/src/data/NullProvider.js
@@ -19,6 +19,7 @@ import React from 'react'
import { Icon } from '@blueprintjs/core'
import { Providers, ProviderLabels } from '@/data/Providers'
+// -- warning -- legacy constant will be removed in a future release
const NullProvider = {
id: Providers.NULL, // Unique ID, for a Provider (alphanumeric, lowercase)
enabled: false, // Enabled Flag
diff --git a/config-ui/src/data/Providers.js b/config-ui/src/data/Providers.js
index 9a3eece8..1a4d4f97 100644
--- a/config-ui/src/data/Providers.js
+++ b/config-ui/src/data/Providers.js
@@ -26,12 +26,17 @@ import { ReactComponent as AzureProviderIcon } from '@/images/integrations/azure
import { ReactComponent as BitbucketProviderIcon } from '@/images/integrations/bitbucket.svg'
import { ReactComponent as GiteeProviderIcon } from '@/images/integrations/gitee.svg'
import { RateLimitTooltip } from '@/data/ConnectionTooltips.js'
-// import GitExtractorIcon from '@/images/git.png'
-// import RefDiffIcon from '@/images/git-diff.png'
import FeishuIcon from '@/images/feishu.png'
-// import DBTIcon from '@/images/dbt.png'
-// import AEIcon from '@/images/ae.png'
+/**
+ * !! WARNING !! DO NOT USE (DEVELOPMENT USE ONLY)
+ * Provider Configuration now managed by Plugin Registry,
+ * This functionality is being replaced by the Integrations Manager Hook!
+ * see src/hooks/useIntegrations.jsx for more information.
+ * ---------------------------------------------------------------
+ */
+
+// @note: replaced by Integrations Hook
const Providers = {
NULL: 'null',
GITLAB: 'gitlab',
@@ -51,12 +56,14 @@ const Providers = {
DORA: 'dora' // (not a true provider)
}
+// @note: replaced by Integrations Hook
const ProviderTypes = {
PLUGIN: 'plugin',
INTEGRATION: 'integration',
PIPELINE: 'pipeline'
}
+// @note: replaced by Integrations Hook
const ProviderLabels = {
NULL: 'NullProvider',
GITLAB: 'GitLab',
@@ -76,6 +83,7 @@ const ProviderLabels = {
DORA: 'DORA' // (not a true provider)
}
+// @note: replaced by Integrations Hook and/or delete
const ProviderConnectionLimits = {
// (All providers are mult-connection, no source limits defined)
// jenkins: null,
@@ -84,8 +92,8 @@ const ProviderConnectionLimits = {
// gitlab: null
}
-// NOTE: Not all fields may be referenced/displayed for a provider,
-// ie. JIRA prefers $token over $username and $password
+// @note: replaced by Integrations Hook
+// @todo: handle tooltips dynamically at form layer
const ProviderFormLabels = {
null: {
name: 'Connection Name',
@@ -262,6 +270,7 @@ const ProviderFormLabels = {
}
}
+// @note: replaced by Integrations Hook
const ProviderFormPlaceholders = {
null: {
name: 'eg. Enter Instance Name',
@@ -346,6 +355,7 @@ const ProviderFormPlaceholders = {
}
}
+// @note: replaced by Integrations Hook
const ProviderIcons = {
[Providers.GITLAB]: (w, h) => (
<GitlabProviderIcon width={w || 24} height={h || 24} />
@@ -381,6 +391,7 @@ const ProviderIcons = {
)
}
+// @note: migrated to @data/ConnectionStatus
const ConnectionStatus = {
OFFLINE: 0,
ONLINE: 1,
@@ -388,6 +399,7 @@ const ConnectionStatus = {
TESTING: 3
}
+// @note: migrated to @data/ConnectionStatus
const ConnectionStatusLabels = {
[ConnectionStatus.OFFLINE]: 'Offline',
[ConnectionStatus.ONLINE]: 'Online',
diff --git a/config-ui/src/data/integrations.jsx b/config-ui/src/data/integrations.jsx
index 375d4ab8..1a10f7aa 100644
--- a/config-ui/src/data/integrations.jsx
+++ b/config-ui/src/data/integrations.jsx
@@ -37,6 +37,7 @@ import { ReactComponent as GiteeProvider } from '@/images/integrations/gitee.svg
// import RefDiffProvider from '@/images/git-diff.png'
// import { ReactComponent as NullProvider } from '@/images/integrations/null.svg'
+// @todo: TO-BE replaced with Integrations Hook
const integrationsData = [
{
id: Providers.GITLAB,
@@ -208,6 +209,7 @@ const integrationsData = [
}
]
+// @todo: deprecate this var, used for legacy V11 Pipeline
const pluginsData = [
{
id: Providers.GITEXTRACTOR,
diff --git a/config-ui/src/hooks/data-scope/useTransformationsManager.jsx b/config-ui/src/hooks/data-scope/useTransformationsManager.jsx
index c846ca99..a5205666 100644
--- a/config-ui/src/hooks/data-scope/useTransformationsManager.jsx
+++ b/config-ui/src/hooks/data-scope/useTransformationsManager.jsx
@@ -15,103 +15,108 @@
* limitations under the License.
*
*/
-import { useCallback, useState } from 'react'
-import { Providers } from '@/data/Providers'
+import { useCallback, useState, useContext } from 'react'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
import TransformationSettings from '@/models/TransformationSettings'
import { isEqual } from 'lodash'
-// TODO separate to each plugin
-const getDefaultTransformations = (provider) => {
- let transforms = {}
- switch (provider) {
- case Providers.GITHUB:
- transforms = {
- prType: '',
- prComponent: '',
- prBodyClosePattern: '',
- issueSeverity: '',
- issueComponent: '',
- issuePriority: '',
- issueTypeRequirement: '',
- issueTypeBug: '',
- issueTypeIncident: '',
- refdiff: null,
- productionPattern: '',
- deploymentPattern: ''
- // stagingPattern: '',
- // testingPattern: ''
- }
- break
- case Providers.JIRA:
- transforms = {
- epicKeyField: '',
- typeMappings: {},
- storyPointField: '',
- remotelinkCommitShaPattern: '',
- bugTags: [],
- incidentTags: [],
- requirementTags: [],
- // @todo: verify if jira utilizes deploy tag(s)?
- productionPattern: '',
- deploymentPattern: ''
- // stagingPattern: '',
- // testingPattern: ''
- }
- break
- case Providers.JENKINS:
- transforms = {
- productionPattern: '',
- deploymentPattern: ''
- // stagingPattern: '',
- // testingPattern: ''
- }
- break
- case Providers.GITLAB:
- transforms = {
- productionPattern: '',
- deploymentPattern: ''
- // stagingPattern: '',
- // testingPattern: ''
- }
- break
- case Providers.TAPD:
- // @todo: complete tapd transforms #2673
- transforms = {
- issueTypeRequirement: '',
- issueTypeBug: '',
- issueTypeIncident: '',
- productionPattern: '',
- deploymentPattern: ''
- // stagingPattern: '',
- // testingPattern: ''
- }
- break
- }
- return transforms
-}
-
// manage transformations in one place
const useTransformationsManager = () => {
+ const { Providers, ProviderTransformations, Integrations } =
+ useContext(IntegrationsContext)
const [transformations, setTransformations] = useState({})
- const generateKey = (
- connectionProvider,
- connectionId,
- projectNameOrBoard
- ) => {
- let key = `not-distinguished`
- switch (connectionProvider) {
- case Providers.GITHUB:
- case Providers.GITLAB:
- case Providers.JENKINS:
- key = projectNameOrBoard?.id
- break
- case Providers.JIRA:
- key = projectNameOrBoard?.id
- break
- }
- return `${connectionProvider}/${connectionId}/${key}`
- }
+ const getDefaultTransformations = useCallback(
+ (provider) => {
+ // let transforms = {}
+ const transforms = ProviderTransformations[provider] || {}
+ // @note: Default Transformations configured in Plugin Registry! (see @src/registry/plugins)
+ // switch (provider) {
+ // case Providers.GITHUB:
+ // transforms = {
+ // prType: '',
+ // prComponent: '',
+ // prBodyClosePattern: '',
+ // issueSeverity: '',
+ // issueComponent: '',
+ // issuePriority: '',
+ // issueTypeRequirement: '',
+ // issueTypeBug: '',
+ // issueTypeIncident: '',
+ // refdiff: null,
+ // productionPattern: '',
+ // deploymentPattern: ''
+ // // stagingPattern: '',
+ // // testingPattern: ''
+ // }
+ // break
+ // case Providers.JIRA:
+ // transforms = {
+ // epicKeyField: '',
+ // typeMappings: {},
+ // storyPointField: '',
+ // remotelinkCommitShaPattern: '',
+ // bugTags: [],
+ // incidentTags: [],
+ // requirementTags: [],
+ // // @todo: verify if jira utilizes deploy tag(s)?
+ // productionPattern: '',
+ // deploymentPattern: ''
+ // // stagingPattern: '',
+ // // testingPattern: ''
+ // }
+ // break
+ // case Providers.JENKINS:
+ // transforms = {
+ // productionPattern: '',
+ // deploymentPattern: ''
+ // // stagingPattern: '',
+ // // testingPattern: ''
+ // }
+ // break
+ // case Providers.GITLAB:
+ // transforms = {
+ // productionPattern: '',
+ // deploymentPattern: ''
+ // // stagingPattern: '',
+ // // testingPattern: ''
+ // }
+ // break
+ // case Providers.TAPD:
+ // // @todo: complete tapd transforms #2673
+ // transforms = {
+ // issueTypeRequirement: '',
+ // issueTypeBug: '',
+ // issueTypeIncident: '',
+ // productionPattern: '',
+ // deploymentPattern: ''
+ // // stagingPattern: '',
+ // // testingPattern: ''
+ // }
+ // break
+ // }
+ return transforms
+ },
+ [ProviderTransformations]
+ )
+
+ const generateKey = useCallback(
+ (connectionProvider, connectionId, entity) => {
+ let key = `not-distinguished`
+ key = entity ? entity?.getConfiguredEntityId() : key
+ console.info(
+ '>> GENERATING TRANSFORMATION KEY FOR ENTITY...',
+ connectionProvider,
+ connectionId,
+ entity,
+ key,
+ `${connectionProvider}/${connectionId}/${key}`
+ )
+ return `${connectionProvider}/${connectionId}/${key}`
+ },
+ []
+ )
// change some setting in specific connection's specific transformation
const changeTransformationSettings = useCallback(
@@ -134,7 +139,7 @@ const useTransformationsManager = () => {
})
}))
},
- [setTransformations]
+ [setTransformations, generateKey]
)
// set a default value for connection's specific transformation
@@ -158,7 +163,12 @@ const useTransformationsManager = () => {
}))
}
},
- [setTransformations, transformations]
+ [
+ setTransformations,
+ generateKey,
+ getDefaultTransformations,
+ transformations
+ ]
)
// get specific connection's specific transformation
@@ -178,7 +188,27 @@ const useTransformationsManager = () => {
)
return transformations[key]
},
- [transformations]
+ [transformations, generateKey]
+ )
+
+ // get provider's specific scope options
+ // @todo: refactor "projectNameOrBoard" to "entity" in all areas
+ const getTransformationScopeOptions = useCallback(
+ (connectionProvider, projectNameOrBoard) => {
+ const key = generateKey(connectionProvider, projectNameOrBoard)
+ const plugin = Integrations.find((p) => p.id === connectionProvider)
+ console.debug(
+ '>> useTransformationsManager.getScopeOptions...',
+
+ connectionProvider,
+ projectNameOrBoard
+ )
+ return plugin &&
+ typeof plugin?.getDefaultTransformationScopeOptions === 'function'
+ ? plugin.getDefaultTransformationScopeOptions(projectNameOrBoard)
+ : {}
+ },
+ [Integrations, generateKey]
)
// clear connection's transformation
@@ -198,7 +228,7 @@ const useTransformationsManager = () => {
[key]: null
}))
},
- [setTransformations]
+ [setTransformations, generateKey]
)
// check connection's transformation is changed
@@ -221,11 +251,12 @@ const useTransformationsManager = () => {
)
return !isEqual(defaultTransform, storedTransform)
},
- [transformations]
+ [transformations, generateKey, getDefaultTransformations]
)
return {
getTransformation,
+ getTransformationScopeOptions,
changeTransformationSettings,
initializeDefaultTransformation,
clearTransformationSettings,
diff --git a/config-ui/src/hooks/useBlueprintValidation.jsx b/config-ui/src/hooks/useBlueprintValidation.jsx
index 13dd1faf..c6b2a695 100644
--- a/config-ui/src/hooks/useBlueprintValidation.jsx
+++ b/config-ui/src/hooks/useBlueprintValidation.jsx
@@ -15,10 +15,11 @@
* limitations under the License.
*
*/
-import { useCallback, useEffect, useState } from 'react'
+import { useCallback, useEffect, useState, useContext } from 'react'
import parser from 'cron-parser'
import { BlueprintMode } from '@/data/NullBlueprint'
-import { Providers } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
function useBlueprintValidation({
name,
@@ -35,6 +36,8 @@ function useBlueprintValidation({
activeProvider = null,
activeConnection = null
}) {
+ const { Providers } = useContext(IntegrationsContext)
+
const [errors, setErrors] = useState([])
const [isValid, setIsValid] = useState(false)
diff --git a/config-ui/src/hooks/useConnectionManager.jsx b/config-ui/src/hooks/useConnectionManager.jsx
index e7b90404..25c112c0 100644
--- a/config-ui/src/hooks/useConnectionManager.jsx
+++ b/config-ui/src/hooks/useConnectionManager.jsx
@@ -23,12 +23,11 @@ import request from '@/utils/request'
import Connection from '@/models/Connection'
import ProviderListConnection from '@/models/ProviderListConnection'
import {
- Providers,
- ProviderConnectionLimits,
ConnectionStatus,
ConnectionStatusLabels
-} from '@/data/Providers'
+} from '@/data/ConnectionStatus'
+import useIntegrations from '@/hooks/useIntegrations'
import useNetworkOfflineMode from '@/hooks/useNetworkOfflineMode'
function useConnectionManager(
@@ -38,6 +37,12 @@ function useConnectionManager(
const history = useHistory()
const { handleOfflineMode } = useNetworkOfflineMode()
+ const {
+ integrations: Integrations,
+ Providers,
+ ProviderConnectionLimits
+ } = useIntegrations()
+
const [provider, setProvider] = useState(activeProvider)
const [name, setName] = useState()
// @todo: refactor to endpoint and setEndpoint
@@ -71,9 +76,9 @@ function useConnectionManager(
const connectionCount = useMemo(() => allConnections.length, [allConnections])
const connectionLimitReached = useMemo(
() =>
- sourceLimits[provider?.id] &&
- connectionCount >= sourceLimits[provider?.id],
- [provider?.id, sourceLimits, connectionCount]
+ ProviderConnectionLimits[provider?.id] &&
+ connectionCount >= ProviderConnectionLimits[provider?.id],
+ [provider?.id, ProviderConnectionLimits, connectionCount]
)
const [connectionsList, setConnectionsList] = useState([])
@@ -165,7 +170,7 @@ function useConnectionManager(
}
runTest()
},
- [provider?.id, connectionTestPayload]
+ [provider?.id, connectionTestPayload, Providers.GITHUB]
)
const notifyConnectionSaveSuccess = useCallback(
@@ -348,33 +353,12 @@ function useConnectionManager(
console.log('>> FETCHING ALL CONNECTION SOURCES')
let c = null
if (allSources) {
- // @todo: build promises dynamically from $integrationsData
- const aC = await Promise.all([
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.JIRA}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.GITLAB}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.JENKINS}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.GITHUB}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.TAPD}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.AZURE}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.BITBUCKET}/connections`
- ),
- request.get(
- `${DEVLAKE_ENDPOINT}/plugins/${Providers.GITEE}/connections`
+ // Fetch All Provider Integrations from IM Hook
+ const aC = await Promise.all(
+ Integrations.map((p) =>
+ request.get(`${DEVLAKE_ENDPOINT}/plugins/${p?.id}/connections`)
)
- ])
+ )
const builtConnections = aC.map((providerResponse) =>
[]
.concat(
@@ -441,7 +425,7 @@ function useConnectionManager(
handleOfflineMode(e.response?.status, e.response)
}
},
- [provider?.id, handleOfflineMode]
+ [provider?.id, handleOfflineMode, Integrations]
)
const deleteConnection = useCallback(
diff --git a/config-ui/src/hooks/useConnectionValidation.jsx b/config-ui/src/hooks/useConnectionValidation.jsx
index 651a5b26..fbf109a6 100644
--- a/config-ui/src/hooks/useConnectionValidation.jsx
+++ b/config-ui/src/hooks/useConnectionValidation.jsx
@@ -15,8 +15,9 @@
* limitations under the License.
*
*/
-import { useState, useEffect, useCallback } from 'react'
-import { Providers } from '@/data/Providers'
+import { useState, useEffect, useCallback, useContext } from 'react'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
function useConnectionValidation({
activeProvider,
@@ -28,6 +29,8 @@ function useConnectionValidation({
username,
password
}) {
+ const { Providers } = useContext(IntegrationsContext)
+
const [errors, setErrors] = useState([])
const [isValid, setIsValid] = useState(false)
const [validURIs, setValidURIs] = useState(['http://', 'https://'])
@@ -105,6 +108,7 @@ function useConnectionValidation({
setErrors(errs)
}, [
+ Providers,
name,
endpointUrl,
proxy,
diff --git a/config-ui/src/hooks/useDataScopesManager.jsx b/config-ui/src/hooks/useDataScopesManager.jsx
index c7b84b9e..72c0d476 100644
--- a/config-ui/src/hooks/useDataScopesManager.jsx
+++ b/config-ui/src/hooks/useDataScopesManager.jsx
@@ -19,13 +19,13 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import { BlueprintMode } from '@/data/NullBlueprint'
import { DEFAULT_DATA_ENTITIES } from '@/data/BlueprintWorkflow'
import { integrationsData } from '@/data/integrations'
-import TransformationSettings from '@/models/TransformationSettings'
+// import TransformationSettings from '@/models/TransformationSettings'
import JiraBoard from '@/models/JiraBoard'
import GitHubProject from '@/models/GithubProject'
import GitlabProject from '@/models/GitlabProject'
-import { ProviderIcons, ProviderLabels, Providers } from '@/data/Providers'
import { DataScopeModes } from '@/data/DataScopes'
import JenkinsJob from '@/models/JenkinsJob'
+import useIntegrations from '@/hooks/useIntegrations'
import useTransformationsManager from '@/hooks/data-scope/useTransformationsManager'
function useDataScopesManager({
@@ -35,6 +35,13 @@ function useDataScopesManager({
/* connection, */ settings = {},
setSettings = () => {}
}) {
+ const {
+ integrations: Integrations,
+ Providers,
+ ProviderLabels,
+ ProviderIcons
+ } = useIntegrations()
+
const [connections, setConnections] = useState([])
const [newConnections, setNewConnections] = useState([])
@@ -53,6 +60,7 @@ function useDataScopesManager({
const [entities, setEntities] = useState({})
const {
getTransformation,
+ getTransformationScopeOptions,
changeTransformationSettings,
initializeDefaultTransformation,
clearTransformationSettings,
@@ -88,7 +96,6 @@ function useDataScopesManager({
]
)
- // @todo: generate scopes dynamically from $integrationsData (in future Integrations Hook [plugin registry])
const createProviderScopes = useCallback(
(
providerId,
@@ -108,68 +115,88 @@ function useDataScopesManager({
...defaultScope,
entities: entities[connection.id]?.map((entity) => entity.value) || []
}
- switch (providerId) {
- case Providers.JIRA:
- newScope = boards[connection.id]?.map((b) => ({
- ...newScope,
- options: {
- boardId: Number(b?.value),
- title: b.title
- // @todo: verify initial value of since date for jira provider
- // since: new Date(),
- },
- transformation: {
- ...getTransformation(connection?.providerId, connection?.id, b)
- }
- }))
- break
- case Providers.GITLAB:
- newScope = projects[connection.id]?.map((p) => ({
- ...newScope,
- options: {
- projectId: Number(p.value),
- title: p.title
- },
- transformation: {
- ...getTransformation(connection?.providerId, connection?.id, p)
- }
- }))
- break
- case Providers.JENKINS:
- newScope = projects[connection.id]?.map((p) => ({
- ...newScope,
- options: {
- jobName: p.value
- },
- transformation: {
- ...getTransformation(connection?.providerId, connection?.id, p)
- }
- }))
- break
- case Providers.GITHUB:
- newScope = projects[connection.id]?.map((p) => ({
- ...newScope,
- options: {
- owner: p.value.split('/')[0],
- repo: p.value.split('/')[1]
- },
- transformation: {
- ...getTransformation(connection?.providerId, connection?.id, p)
- }
- }))
- break
- case Providers.TAPD:
- newScope = {
- ...newScope
- // options: {
- // },
- // transformation: {},
- }
- break
- }
+ // Generate scopes Dynamically for all Project/Board/Job/... Entities
+ // @todo: refactor again when boards & projects get merged...
+ newScope = [
+ ...(Array.isArray(boards[connection.id]) ? boards[connection.id] : []),
+ ...(Array.isArray(projects[connection.id])
+ ? projects[connection.id]
+ : [])
+ ].map((e) => ({
+ ...newScope,
+ options: {
+ ...getTransformationScopeOptions(connection?.providerId, e)
+ },
+ transformation: {
+ ...getTransformation(connection?.providerId, connection?.id, e)
+ }
+ }))
+ // switch (providerId) {
+ // case Providers.JIRA:
+ // newScope = boards[connection.id]?.map((b) => ({
+ // ...newScope,
+ // options: {
+ // boardId: Number(b?.value),
+ // title: b.title
+ // // @todo: verify initial value of since date for jira provider
+ // // since: new Date(),
+ // },
+ // transformation: {
+ // ...getTransformation(connection?.providerId, connection?.id, b)
+ // }
+ // }))
+ // break
+ // case Providers.GITLAB:
+ // newScope = projects[connection.id]?.map((p) => ({
+ // ...newScope,
+ // options: {
+ // projectId: Number(p.value),
+ // title: p.title
+ // },
+ // transformation: {
+ // ...getTransformation(connection?.providerId, connection?.id, p)
+ // }
+ // }))
+ // break
+ // case Providers.JENKINS:
+ // newScope = projects[connection.id]?.map((p) => ({
+ // ...newScope,
+ // options: {
+ // jobName: p.value
+ // },
+ // transformation: {
+ // ...getTransformation(connection?.providerId, connection?.id, p)
+ // }
+ // }))
+ // break
+ // case Providers.GITHUB:
+ // newScope = projects[connection.id]?.map((p) => ({
+ // ...newScope,
+ // options: {
+ // owner: p.value.split('/')[0],
+ // repo: p.value.split('/')[1]
+ // },
+ // transformation: {
+ // ...getTransformation(connection?.providerId, connection?.id, p)
+ // }
+ // }))
+ // break
+ // case Providers.TAPD:
+ // newScope = {
+ // ...newScope
+ // // options: {
+ // // },
+ // // transformation: {},
+ // }
+ // break
+ // }
return Array.isArray(newScope) ? newScope.flat() : [newScope]
},
- [getTransformation]
+ [
+ getTransformation,
+ getTransformationScopeOptions
+ // Providers
+ ]
)
const createProviderConnections = useCallback(
@@ -282,7 +309,7 @@ function useDataScopesManager({
return []
}
},
- [getGithubProjects, getGitlabProjects, getJenkinsProjects]
+ [getGithubProjects, getGitlabProjects, getJenkinsProjects, Providers]
)
const getAdvancedGithubProjects = useCallback(
@@ -297,7 +324,7 @@ function useDataScopesManager({
})
]
: [],
- []
+ [Providers.GITHUB]
)
const getAdvancedGitlabProjects = useCallback(
@@ -312,7 +339,7 @@ function useDataScopesManager({
})
]
: [],
- []
+ [Providers.GITLAB]
)
const getAdvancedJiraBoards = useCallback(
@@ -327,7 +354,7 @@ function useDataScopesManager({
})
]
: [],
- []
+ [Providers.JIRA]
)
// (altered version from PR No. 2926)
@@ -363,27 +390,16 @@ function useDataScopesManager({
[]
)
- const getDefaultEntities = useCallback((providerId) => {
- let entities = []
- switch (providerId) {
- case Providers.GITHUB:
- case Providers.GITLAB:
- entities = DEFAULT_DATA_ENTITIES
- break
- case Providers.JIRA:
- entities = DEFAULT_DATA_ENTITIES.filter(
- (d) => d.name === 'issue-tracking' || d.name === 'cross-domain'
- )
- break
- case Providers.JENKINS:
- entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'ci-cd')
- break
- case Providers.TAPD:
- entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'ci-cd')
- break
- }
- return entities
- }, [])
+ const getDefaultEntities = useCallback(
+ (providerId) => {
+ console.log('GET ENTITIES FOR PROVIDER =', providerId)
+ let entities = []
+ const plugin = Integrations.find((p) => p.id === providerId)
+ entities = plugin ? plugin?.getDataEntities() : []
+ return entities
+ },
+ [Integrations]
+ )
const createNormalConnection = useCallback(
(
@@ -448,7 +464,7 @@ function useDataScopesManager({
stage: 1,
totalStages: 1
}),
- [getProjects]
+ [getProjects, ProviderLabels, ProviderIcons, Providers.JIRA]
)
const createAdvancedConnection = useCallback(
@@ -521,7 +537,11 @@ function useDataScopesManager({
getAdvancedGithubProjects,
getAdvancedGitlabProjects,
getAdvancedJiraBoards,
- getDefaultEntities
+ getDefaultEntities,
+ ProviderIcons,
+ ProviderLabels,
+ Providers.GITLAB,
+ Providers.JIRA
]
)
@@ -611,7 +631,7 @@ function useDataScopesManager({
)
break
}
- }, [connection, changeTransformationSettings])
+ }, [connection, changeTransformationSettings, Providers])
useEffect(() => {
console.log('>>>>> DATA SCOPES MANAGER: Connection List...', connections)
@@ -682,6 +702,13 @@ function useDataScopesManager({
)
}, [connection])
+ useEffect(() => {
+ console.log(
+ '>>>>> DATA SCOPES MANAGER: SELECTED NEW CONNECTIONS...',
+ newConnections
+ )
+ }, [newConnections])
+
return {
connections,
newConnections,
diff --git a/config-ui/src/hooks/useIntegrations.jsx b/config-ui/src/hooks/useIntegrations.jsx
index 6f831b43..a3c5f884 100644
--- a/config-ui/src/hooks/useIntegrations.jsx
+++ b/config-ui/src/hooks/useIntegrations.jsx
@@ -15,17 +15,308 @@
* limitations under the License.
*
*/
-import { useState, useEffect } from 'react'
-import { integrationsData } from '@/data/integrations'
+import React, { useState, useEffect, useCallback, useMemo } from 'react'
-function useIntegrations(data = []) {
- const [integrations, setIntegrations] = useState(integrationsData)
+import Plugin from '@/models/Plugin'
- useEffect(() => {
- // setIntegrations(integrationsData)
+// LOCAL PLUGIN REGISTRY
+// "integration" Plugins a.k.a "Providers"
+import JiraPlugin from '@/registry/plugins/jira.json'
+import GitHubPlugin from '@/registry/plugins/github.json'
+import GitLabPlugin from '@/registry/plugins/gitlab.json'
+import JenkinsPlugin from '@/registry/plugins/jenkins.json'
+import TapdPlugin from '@/registry/plugins/tapd.json'
+import AzurePlugin from '@/registry/plugins/azure.json'
+import BitbucketPlugin from '@/registry/plugins/bitbucket.json'
+import GiteePlugin from '@/registry/plugins/gitee.json'
+import AePlugin from '@/registry/plugins/ae.json'
+import RefdiffPlugin from '@/registry/plugins/refdiff.json'
+import DbtPlugin from '@/registry/plugins/dbt.json'
+import StarrocksPlugin from '@/registry/plugins/starrocks.json'
+import DoraPlugin from '@/registry/plugins/dora.json'
+
+const ProviderTypes = {
+ PLUGIN: 'plugin',
+ INTEGRATION: 'integration',
+ PIPELINE: 'pipeline',
+ WEBHOOK: 'webhook'
+}
+
+function useIntegrations(
+ pluginRegistry = [
+ JiraPlugin,
+ GitHubPlugin,
+ GitLabPlugin,
+ JenkinsPlugin,
+ TapdPlugin,
+ AzurePlugin,
+ BitbucketPlugin,
+ GiteePlugin,
+ AePlugin,
+ RefdiffPlugin,
+ DbtPlugin,
+ StarrocksPlugin,
+ DoraPlugin
+ ]
+) {
+ const [registry, setRegistry] = useState(pluginRegistry || [])
+ const [plugins, setPlugins] = useState([])
+
+ // @todo: fetch live/dynamic plugins from API
+ const [apiPlugins, setApiPlugins] = useState([])
+ const [apiRegistry, setApiRegistry] = useState([])
+
+ const [activeProvider, setActiveProvider] = useState()
+
+ const integrations = useMemo(
+ () => plugins.filter((p) => p.type === ProviderTypes.INTEGRATION),
+ [plugins]
+ )
+
+ const DataSources = useMemo(
+ () =>
+ integrations.map((P, iDx) => ({
+ id: iDx + 1,
+ name: P.name,
+ title: P.name,
+ value: P.id
+ })),
+ [integrations]
+ )
+
+ const Providers = useMemo(
+ () =>
+ plugins
+ .map((P) => P.id)
+ .reduce(
+ (pV, cV, iDx) => ({ ...pV, [cV.toString()?.toUpperCase()]: cV }),
+ {}
+ ),
+ [plugins]
+ )
+
+ const ProviderLabels = useMemo(
+ () =>
+ plugins
+ .map((P) => P)
+ .reduce(
+ (pV, cV, iDx) => ({
+ ...pV,
+ [cV?.id.toString()?.toUpperCase()]: cV.name
+ }),
+ {}
+ ),
+ [plugins]
+ )
+
+ const ProviderFormLabels = useMemo(
+ () =>
+ integrations
+ .map((P) => P.getConnectionFormLabels())
+ .reduce((pV, cV, iDx) => ({ ...pV, [integrations[iDx]?.id]: cV }), {}),
+ [integrations]
+ )
+
+ const ProviderFormPlaceholders = useMemo(
+ () =>
+ integrations
+ .map((P) => P.getConnectionFormPlaceholders())
+ .reduce((pV, cV, iDx) => ({ ...pV, [integrations[iDx]?.id]: cV }), {}),
+ [integrations]
+ )
+
+ const ProviderFormTooltips = useMemo(
+ () =>
+ integrations
+ .map((P) => P.getConnectionFormTooltips())
+ .reduce((pV, cV, iDx) => ({ ...pV, [integrations[iDx]?.id]: cV }), {}),
+ [integrations]
+ )
+
+ const ProviderIcons = useMemo(
+ () =>
+ integrations
+ .map((p) => p.icon)
+ .reduce(
+ (pV, cV, iDx) => ({
+ ...pV,
+ [integrations[iDx]?.id]: (w, h) => (
+ <img
+ src={'/' + cV}
+ style={{ width: `${w}px`, height: `${h}px` }}
+ />
+ )
+ }),
+ {}
+ ),
+ [integrations]
+ )
+
+ const ProviderConnectionLimits = useMemo(
+ () =>
+ integrations
+ .map((P) => parseInt(P.connectionLimit, 10))
+ .reduce((pV, cV, iDx) => ({ ...pV, [integrations[iDx]?.id]: cV }), {}),
+ [integrations]
+ )
+
+ const ProviderTransformations = useMemo(
+ () =>
+ integrations
+ .map((P) => P.getDefaultTransformations())
+ .reduce((pV, cV, iDx) => ({ ...pV, [integrations[iDx]?.id]: cV }), {}),
+ [integrations]
+ )
+
+ const registerPlugin = useCallback((pluginConfig) => {
+ console.log(
+ '>>> REGISTERING PLUGIN...',
+ `${pluginConfig?.name} [${pluginConfig?.type}]`
+ )
+ // @todo: Validate Plugin before Registration
+ return new Plugin(pluginConfig)
+ }, [])
+
+ const validatePlugin = useCallback((pluginConfig) => {
+ let isValid = false
+ const requiredProperties = [
+ 'id',
+ 'name',
+ 'type',
+ 'enabled',
+ 'multiConnection',
+ 'icon'
+ ]
+ // todo: enhance plugin validation
+ try {
+ console.log('>>> INTEGRATIONS HOOK: VALIDATING PLUGIN...', pluginConfig)
+ JSON.parse(JSON.stringify(pluginConfig))
+ isValid = requiredProperties.every((p) =>
+ Object.prototype.hasOwnProperty.call(pluginConfig, p)
+ )
+ if (!isValid) {
+ console.log(
+ '>>> INTEGRATIONS HOOK: PLUGIN SCHEMA INCOMPLETE, MISSING REQUIRED PROPERTIES!',
+ pluginConfig
+ )
+ }
+ } catch (e) {
+ console.log(
+ '>>> INTEGRATIONS HOOK: PLUGIN VALIDATION FAILED!',
+ e,
+ pluginConfig
+ )
+ }
+ return isValid
}, [])
- return [integrations, setIntegrations]
+ const getPlugin = useCallback(
+ (pluginId) => {
+ return plugins.find((p) => p.id === pluginId)
+ },
+ [plugins]
+ )
+
+ useEffect(() => {
+ console.log(
+ '>>> INTEGRATIONS HOOK: PLUGIN REGISTRY CONFIGURATION!!!',
+ registry
+ )
+ setPlugins((aP) => [
+ // ...aP,
+ ...registry
+ .filter((p) => p.enabled)
+ .filter((p) => validatePlugin(p))
+ .map((p) => registerPlugin(p))
+ ])
+ }, [registry, setPlugins, validatePlugin, registerPlugin])
+
+ useEffect(() => {
+ console.log(
+ '>>> INTEGRATIONS HOOK: REGISTERED PLUGIN OBJECT CLASSES...',
+ plugins
+ )
+ setActiveProvider(plugins[0])
+ }, [plugins])
+
+ useEffect(() => {
+ console.log(
+ '>>> INTEGRATIONS HOOK: REGISTERED LIVE API PLUGIN OBJECT CLASSES...',
+ apiPlugins
+ )
+ }, [apiPlugins])
+
+ useEffect(() => {
+ console.log('>>> INTEGRATIONS HOOK: ACTIVE PROVIDER..', activeProvider)
+ }, [activeProvider])
+
+ useEffect(() => {
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDERS CONFIGURATION LIST ...',
+ Providers
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER LABELS CONFIGURATION LIST ...',
+ ProviderLabels
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER CONFIGURATION CONNECTION FORM LABELS..',
+ ProviderFormLabels
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER CONFIGURATION CONNECTION FORM PLACEHOLDERS..',
+ ProviderFormPlaceholders
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER CONFIGURATION CONNECTION FORM TOOLTIPS..',
+ ProviderFormTooltips
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER CONFIGURATION PROVIDER ICONS..',
+ ProviderIcons
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER CONNECTION LIMITS...',
+ ProviderConnectionLimits
+ )
+ console.log(
+ '>>> INTEGRATIONS HOOK: PROVIDER DATA SOURCES LIST...',
+ DataSources
+ )
+ }, [
+ activeProvider,
+ Providers,
+ ProviderLabels,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderConnectionLimits,
+ ProviderFormTooltips,
+ ProviderIcons,
+ DataSources
+ ])
+
+ return {
+ activeProvider,
+ registry,
+ plugins,
+ apiPlugins,
+ apiRegistry,
+ integrations,
+ DataSources,
+ Providers,
+ ProviderLabels,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderFormTooltips,
+ ProviderIcons,
+ ProviderConnectionLimits,
+ ProviderTypes,
+ ProviderTransformations,
+ setRegistry,
+ setApiRegistry,
+ setActiveProvider,
+ getPlugin
+ }
}
export default useIntegrations
diff --git a/config-ui/src/hooks/useJIRA.jsx b/config-ui/src/hooks/useJIRA.jsx
index eccaa5be..08b36da5 100644
--- a/config-ui/src/hooks/useJIRA.jsx
+++ b/config-ui/src/hooks/useJIRA.jsx
@@ -18,8 +18,6 @@
import { useEffect, useState, useCallback } from 'react'
import request from '@/utils/request'
import { ToastNotification } from '@/components/Toast'
-import { Providers } from '@/data/Providers'
-import DataScopeConnection from '@/models/DataScopeConnection'
const useJIRA = (
{ apiProxyPath, issuesEndpoint, fieldsEndpoint, boardsEndpoint },
@@ -41,9 +39,6 @@ const useJIRA = (
const [error, setError] = useState()
const fetchIssueTypes = useCallback(() => {
- // if (activeConnection?.plugin !== Providers.JIRA) {
- // return
- // }
try {
if (apiProxyPath.includes('null') || !activeConnection?.connectionId) {
throw new Error('Connection ID is Null')
@@ -82,9 +77,6 @@ const useJIRA = (
}, [issuesEndpoint, activeConnection, apiProxyPath])
const fetchFields = useCallback(() => {
- // if (activeConnection?.plugin !== Providers.JIRA) {
- // return
- // }
try {
if (apiProxyPath.includes('null') || !activeConnection?.connectionId) {
throw new Error('Connection ID is Null')
@@ -124,9 +116,6 @@ const useJIRA = (
const fetchBoards = useCallback(
async (search, callback = () => {}) => {
- // if (activeConnection?.plugin !== Providers.JIRA) {
- // return
- // }
try {
if (apiProxyPath.includes('null') || !activeConnection?.connectionId) {
throw new Error('Connection ID is Null')
@@ -273,10 +262,6 @@ const useJIRA = (
)
}, [boardsResponse])
- // useEffect(() => {
- // console.log('>>> ALL JIRA RESOURCES!', allResources)
- // }, [allResources])
-
useEffect(() => {
console.log('>>> JIRA API PROXY: FIELD SELECTOR LIST DATA', fields)
}, [fields])
@@ -291,10 +276,6 @@ const useJIRA = (
}
}, [error])
- // useEffect(() => {
- // console.log('>>> JIRA PROXY ACTIVE CONNECTION...', activeConnection)
- // }, [activeConnection])
-
return {
isFetching,
fetchFields,
diff --git a/config-ui/src/hooks/usePipelineManager.jsx b/config-ui/src/hooks/usePipelineManager.jsx
index aa1a60f8..5a97c7ec 100644
--- a/config-ui/src/hooks/usePipelineManager.jsx
+++ b/config-ui/src/hooks/usePipelineManager.jsx
@@ -15,12 +15,13 @@
* limitations under the License.
*
*/
-import { useState, useEffect, useCallback, useMemo } from 'react'
+import { useState, useEffect, useCallback, useMemo, useContext } from 'react'
import { DEVLAKE_ENDPOINT } from '@/utils/config'
import request from '@/utils/request'
import { NullPipelineRun } from '@/data/NullPipelineRun'
import { ToastNotification } from '@/components/Toast'
-import { Providers } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
import { Intent } from '@blueprintjs/core'
// import { integrationsData } from '@/data/integrations'
@@ -28,6 +29,7 @@ function usePipelineManager(
myPipelineName = `COLLECTION ${Date.now()}`,
initialTasks = []
) {
+ const { Providers } = useContext(IntegrationsContext)
// const [integrations, setIntegrations] = useState(integrationsData)
const [pipelineName, setPipelineName] = useState(
myPipelineName ?? `COLLECTION ${Date.now()}`
@@ -47,18 +49,9 @@ function usePipelineManager(
const [activePipeline, setActivePipeline] = useState(NullPipelineRun)
const [lastRunId, setLastRunId] = useState(null)
const [pipelineRun, setPipelineRun] = useState(NullPipelineRun)
- const [allowedProviders, setAllowedProviders] = useState([
- Providers.JIRA,
- Providers.GITLAB,
- Providers.JENKINS,
- Providers.GITHUB,
- Providers.REFDIFF,
- Providers.GITEXTRACTOR,
- Providers.FEISHU,
- Providers.AE,
- Providers.DBT,
- Providers.TAPD
- ])
+ const [allowedProviders, setAllowedProviders] = useState(
+ Object.keys(Providers)
+ )
const PIPELINES_ENDPOINT = useMemo(() => `${DEVLAKE_ENDPOINT}/pipelines`, [])
const [logfile, setLogfile] = useState('logging.tar.gz')
diff --git a/config-ui/src/hooks/usePipelineValidation.jsx b/config-ui/src/hooks/usePipelineValidation.jsx
index f1cd789b..1fabba0a 100644
--- a/config-ui/src/hooks/usePipelineValidation.jsx
+++ b/config-ui/src/hooks/usePipelineValidation.jsx
@@ -15,8 +15,9 @@
* limitations under the License.
*
*/
-import { useCallback, useEffect, useState } from 'react'
-import { Providers } from '@/data/Providers'
+import { useCallback, useEffect, useState, useContext } from 'react'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
import { BlueprintMode } from '@/data/NullBlueprint'
function usePipelineValidation({
@@ -43,22 +44,13 @@ function usePipelineValidation({
entities = [],
rawConfiguration
}) {
+ const { Providers } = useContext(IntegrationsContext)
const [errors, setErrors] = useState([])
const [isValid, setIsValid] = useState(false)
const [detectedProviders, setDetectedProviders] = useState([])
- const [allowedProviders, setAllowedProviders] = useState([
- Providers.JIRA,
- Providers.GITLAB,
- Providers.JENKINS,
- Providers.GITHUB,
- Providers.REFDIFF,
- Providers.GITEXTRACTOR,
- Providers.FEISHU,
- Providers.AE,
- Providers.DBT,
- Providers.STARROCKS,
- Providers.TAPD
- ])
+ const [allowedProviders, setAllowedProviders] = useState(
+ Object.keys(Providers)
+ )
const clear = () => {
setErrors([])
diff --git a/config-ui/src/index.js b/config-ui/src/index.js
index 0a1693a5..83f0e9df 100644
--- a/config-ui/src/index.js
+++ b/config-ui/src/index.js
@@ -20,10 +20,13 @@ import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { UIContextProvider } from '@/store/UIContext'
+import { IntegrationsContextProvider } from '@/store/integrations-context'
ReactDOM.render(
<UIContextProvider>
- <App />
+ <IntegrationsContextProvider>
+ <App />
+ </IntegrationsContextProvider>
</UIContextProvider>,
document.getElementById('app')
)
diff --git a/config-ui/src/models/DataEntity.js b/config-ui/src/models/DataEntity.js
new file mode 100644
index 00000000..435987b1
--- /dev/null
+++ b/config-ui/src/models/DataEntity.js
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @type {object}
+ */
+const DataEntityTypes = {
+ CODE: 'CODE',
+ TICKET: 'TICKET',
+ CODE_REVIEW: 'CODEREVIEW',
+ CROSSDOMAIN: 'CROSS',
+ DEVOPS: 'CICD'
+ // USER: 'user',
+}
+
+/**
+ * @type {<Array<Object>>}
+ */
+const DataEntityList = [
+ {
+ id: 1,
+ name: 'source-code-management',
+ title: 'Source Code Management',
+ value: DataEntityTypes.CODE
+ },
+ {
+ id: 2,
+ name: 'issue-tracking',
+ title: 'Issue Tracking',
+ value: DataEntityTypes.TICKET
+ },
+ {
+ id: 3,
+ name: 'code-review',
+ title: 'Code Review',
+ value: DataEntityTypes.CODE_REVIEW
+ },
+ {
+ id: 4,
+ name: 'cross-domain',
+ title: 'Crossdomain',
+ value: DataEntityTypes.CROSSDOMAIN
+ },
+ {
+ id: 5,
+ name: 'ci-cd',
+ title: 'CI/CD',
+ value: DataEntityTypes.DEVOPS
+ }
+]
+
+/**
+ * @typedef {object} DataEntity
+ * @property {number} id
+ * @property {string} name
+ * @property {string?} title
+ * @property {string} value
+ * @property {'CODE'|'TICKET'|'CODEREVIEW'|'CROSS'|'CICD'} type
+ */
+class DataEntity {
+ constructor(data = {}) {
+ this.id = data?.type
+ ? DataEntityList.find((e) => e.value === data?.type)?.id
+ : 0
+ this.name = data?.type
+ ? DataEntityList.find((e) => e.value === data?.type)?.name
+ : DataEntityList[0]?.name
+ this.title = data?.type
+ ? DataEntityList.find((e) => e.value === data?.type)?.title
+ : DataEntityList[0]?.title
+ this.value = data?.type
+ ? DataEntityList.find((e) => e.value === data?.type)?.value
+ : DataEntityList[0]?.type
+ this.type = data?.type || DataEntityList[0]?.type
+ }
+
+ get(property) {
+ return this[property]
+ }
+
+ set(property, value) {
+ this[property] = value
+ return this.property
+ }
+}
+
+export default DataEntity
diff --git a/config-ui/src/models/GithubProject.js b/config-ui/src/models/GithubProject.js
index d7aebbd8..bce54912 100644
--- a/config-ui/src/models/GithubProject.js
+++ b/config-ui/src/models/GithubProject.js
@@ -63,6 +63,13 @@ class GitHubProject {
getConfiguredEntityId() {
return this.name.toString() || this.id
}
+
+ getTransformationScopeOptions() {
+ return {
+ owner: this.owner,
+ repo: this.repo
+ }
+ }
}
export default GitHubProject
diff --git a/config-ui/src/models/GitlabProject.js b/config-ui/src/models/GitlabProject.js
index 6ab3b0d9..70d8f22b 100644
--- a/config-ui/src/models/GitlabProject.js
+++ b/config-ui/src/models/GitlabProject.js
@@ -102,6 +102,13 @@ class GitlabProject {
getConfiguredEntityId() {
return this.id
}
+
+ getTransformationScopeOptions() {
+ return {
+ projectId: this.id,
+ title: this.title
+ }
+ }
}
export default GitlabProject
diff --git a/config-ui/src/models/JenkinsJob.js b/config-ui/src/models/JenkinsJob.js
index a5fe7c8d..f2bc3ade 100644
--- a/config-ui/src/models/JenkinsJob.js
+++ b/config-ui/src/models/JenkinsJob.js
@@ -53,6 +53,12 @@ class JenkinsJob {
getConfiguredEntityId() {
return this.name.toString() || this.id
}
+
+ getTransformationScopeOptions() {
+ return {
+ jobName: this.value
+ }
+ }
}
export default JenkinsJob
diff --git a/config-ui/src/models/JiraBoard.js b/config-ui/src/models/JiraBoard.js
index 89573bf0..9ccaa98f 100644
--- a/config-ui/src/models/JiraBoard.js
+++ b/config-ui/src/models/JiraBoard.js
@@ -72,6 +72,13 @@ class JiraBoard {
getConfiguredEntityId() {
return this.id
}
+
+ getTransformationScopeOptions() {
+ return {
+ boardId: this.id,
+ title: this.title
+ }
+ }
}
export default JiraBoard
diff --git a/config-ui/src/models/JenkinsJob.js b/config-ui/src/models/MenuItem.js
similarity index 58%
copy from config-ui/src/models/JenkinsJob.js
copy to config-ui/src/models/MenuItem.js
index a5fe7c8d..eb77d256 100644
--- a/config-ui/src/models/JenkinsJob.js
+++ b/config-ui/src/models/MenuItem.js
@@ -17,28 +17,27 @@
*/
/**
- * @typedef {object} JenkinsJob
+ * @typedef {object} MenuItem
* @property {number?} id
- * @property {number?} key
- * @property {number?} projectId
- * @property {string|number?} name
- * @property {string|number?} value
- * @property {string|number?} title
- * @property {boolean?} useApi
- * @property {project|board|job?} variant
- * @property {string?} providerId
+ * @property {boolean?} disabled
+ * @property {string?} label
+ * @property {string|Object?} route
+ * @property {boolean?} active
+ * @property {string|Object} icon
+ * @property {<Array<string>>?} classNames
+ * @property {<Array<MenuItem>>?} children
+ * @property {string?} target
*/
-class JenkinsJob {
+class MenuItem {
constructor(data = {}) {
this.id = data?.id || null
- this.key = data?.key || this.id || null
- this.name = data?.name || null
- this.value = data?.value || this.name || this.id || null
- this.title = data?.title || this.name || this.id || null
-
- this.useApi = data?.useApi || true
- this.variant = data?.variant || 'job'
- this.providerId = 'jenkins'
+ this.disabled = !!data?.disabled
+ this.label = data?.label || null
+ this.route = data?.route || '#'
+ this.active = !!data?.active
+ this.icon = data?.icon || null
+ this.classNames = Array.isArray(data?.classNames) ? data?.classNames : []
+ this.target = data?.target || null
}
get(property) {
@@ -49,10 +48,6 @@ class JenkinsJob {
this[property] = value
return this.property
}
-
- getConfiguredEntityId() {
- return this.name.toString() || this.id
- }
}
-export default JenkinsJob
+export default MenuItem
diff --git a/config-ui/src/models/Plugin.js b/config-ui/src/models/Plugin.js
new file mode 100644
index 00000000..cb887a3d
--- /dev/null
+++ b/config-ui/src/models/Plugin.js
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import DataEntity from './DataEntity'
+
+/**
+ * @typedef {object} Plugin
+ * @property {string|number?} id
+ * @property {string} name
+ * @property {string?} description
+ * @property {'integration'|'pipeline'|'plugin'?} type
+ * @property {bool} enabled
+ * @property {bool} isBeta
+ * @property {bool} isProvider
+ * @property {bool} multiConnection
+ * @property {bool} private
+ * @property {object?} icon
+ * @property {object?} connection
+ * @property {object?} settings
+ * @property {number?} connectionLimit
+ * @property {<Array<DataEntity>>?} entities
+ * @property {object?} transformations
+ */
+class Plugin {
+ constructor(data = {}) {
+ this.id = data?.id || Math.random() * 99999
+ this.name = data?.name || 'New Plugin'
+ this.type = data?.type || 'integration'
+ this.description = data?.description || null
+ this.enabled = data?.enabled || false
+ this.isBeta = data?.isBeta || false
+ this.isProvider = data?.isProvider || false
+ this.multiConnection = data?.multiConnection || false
+ this.private = data?.private || false
+ this.icon = data?.icon || null
+ this.connection = data?.connection || null
+ this.connectionLimit = data?.connectionLimit || 0
+ this.entities = data?.entities?.map((e) => new DataEntity({ type: e })) || [
+ new DataEntity({ type: 'CODE' })
+ ]
+ this.transformations = data?.transformations || {
+ scopes: { options: {} },
+ default: {}
+ }
+ }
+
+ get(property) {
+ return this[property]
+ }
+
+ set(property, value) {
+ this[property] = value
+ return this.property
+ }
+
+ getAuthenticationType() {
+ return this.connection?.authentication || 'plain'
+ }
+
+ getConnectionFields() {
+ return this.connection ? this.connection?.fields : {}
+ }
+
+ getConnectionFormLabels() {
+ return this.connection ? this.connection?.labels : {}
+ }
+
+ getConnectionFormPlaceholders() {
+ return this.connection ? this.connection?.placeholders : {}
+ }
+
+ getConnectionFormTooltips() {
+ return this.connection ? this.connection?.tooltips : {}
+ }
+
+ getDefaultTransformations() {
+ return this.transformations?.default || {}
+ }
+
+ getDefaultTransformationScopeOptions(entity) {
+ const scopeOptions = {
+ ...(this.transformations?.scopes?.options || {}),
+ ...(entity && typeof entity.getTransformationScopeOptions === 'function'
+ ? entity.getTransformationScopeOptions()
+ : {})
+ }
+ return scopeOptions
+ }
+
+ getDataEntities() {
+ return this.entities || []
+ }
+}
+
+export default Plugin
diff --git a/config-ui/src/pages/blueprints/blueprint-detail.jsx b/config-ui/src/pages/blueprints/blueprint-detail.jsx
index 96756537..0820a3e2 100644
--- a/config-ui/src/pages/blueprints/blueprint-detail.jsx
+++ b/config-ui/src/pages/blueprints/blueprint-detail.jsx
@@ -41,7 +41,7 @@ import {
} from '@blueprintjs/core'
import { NullBlueprint } from '@/data/NullBlueprint'
import { NullPipelineRun } from '@/data/NullPipelineRun'
-import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
+// import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
import {
StageStatus,
TaskStatus,
@@ -59,6 +59,7 @@ import StageLane from '@/components/pipelines/StageLane'
import { ToastNotification } from '@/components/Toast'
import BlueprintNavigationLinks from '@/components/blueprints/BlueprintNavigationLinks'
+import useIntegrations from '@/hooks/useIntegrations'
import useBlueprintManager from '@/hooks/useBlueprintManager'
import usePipelineManager from '@/hooks/usePipelineManager'
import usePaginator from '@/hooks/usePaginator'
@@ -66,6 +67,18 @@ import usePaginator from '@/hooks/usePaginator'
const BlueprintDetail = (props) => {
// eslint-disable-next-line no-unused-vars
const history = useHistory()
+
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ Providers,
+ ProviderIcons,
+ ProviderLabels,
+ activeProvider,
+ setActiveProvider
+ } = useIntegrations()
+
const { bId } = useParams()
const [blueprintId, setBlueprintId] = useState()
@@ -313,7 +326,7 @@ const BlueprintDetail = (props) => {
plan: blueprint?.plan
})
}
- }, [blueprint, setPipelineSettings])
+ }, [blueprint, setPipelineSettings, ProviderLabels])
useEffect(() => {
console.log('>>>> FETCHED ALL PIPELINES..', pipelines, activeBlueprint?.id)
@@ -440,7 +453,7 @@ const BlueprintDetail = (props) => {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<div
diff --git a/config-ui/src/pages/blueprints/blueprint-settings.jsx b/config-ui/src/pages/blueprints/blueprint-settings.jsx
index ae4d47d7..e4e736c8 100644
--- a/config-ui/src/pages/blueprints/blueprint-settings.jsx
+++ b/config-ui/src/pages/blueprints/blueprint-settings.jsx
@@ -39,13 +39,13 @@ import {
Classes,
Popover
} from '@blueprintjs/core'
-
-import { integrationsData } from '@/data/integrations'
+import useIntegrations from '@/hooks/useIntegrations'
+// import { integrationsData } from '@/data/integrations'
import JiraBoard from '@/models/JiraBoard'
import DataScopeConnection from '@/models/DataScopeConnection'
import { NullBlueprint, BlueprintMode } from '@/data/NullBlueprint'
import { NullPipelineRun } from '@/data/NullPipelineRun'
-import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
+// import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
import { TaskStatus } from '@/data/Task'
import Nav from '@/components/Nav'
@@ -89,7 +89,18 @@ const BlueprintSettings = (props) => {
const history = useHistory()
const { bId } = useParams()
- const [activeProvider, setActiveProvider] = useState(integrationsData[0])
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ Providers,
+ ProviderLabels,
+ ProviderIcons,
+ activeProvider,
+ setActiveProvider
+ } = useIntegrations()
+
+ // const [activeProvider, setActiveProvider] = useState(integrationsData[0])
// @disabled Provided By Data Scopes Manager
// const [activeTransformation, setActiveTransformation] = useState()
@@ -482,7 +493,7 @@ const BlueprintSettings = (props) => {
console.log('>>> MODIFYING DATA CONNECTION SCOPE...', connectionWithScope)
setActiveProvider((aP) =>
connection
- ? integrationsData.find((i) => i.id === connection?.provider)
+ ? Integrations.find((i) => i.id === connection?.provider)
: aP
)
setActiveSetting((aS) => ({
@@ -501,7 +512,9 @@ const BlueprintSettings = (props) => {
connectionsList,
connections,
setScopeConnection,
- setConfiguredConnection
+ setConfiguredConnection,
+ setActiveProvider,
+ Integrations
]
)
@@ -575,7 +588,8 @@ const BlueprintSettings = (props) => {
entities,
configuredConnection,
activeProvider?.id,
- activeBlueprint?.mode
+ activeBlueprint?.mode,
+ Providers
])
// @note: lifted higher to dsm hook
@@ -762,7 +776,8 @@ const BlueprintSettings = (props) => {
getJiraMappedBoards,
setRawConfiguration,
createAdvancedConnection,
- createNormalConnection
+ createNormalConnection,
+ Providers.JIRA
])
useEffect(() => {
@@ -879,7 +894,8 @@ const BlueprintSettings = (props) => {
// activeProvider,
// isFetchingJIRA,
// jiraApiBoards,
- scopeConnection
+ scopeConnection,
+ Providers.JIRA
])
useEffect(() => {
@@ -913,7 +929,8 @@ const BlueprintSettings = (props) => {
scopeConnection?.connectionId,
scopeConnection?.providerId,
getJiraMappedBoards,
- setConnections
+ setConnections,
+ Providers.JIRA
])
useEffect(() => {
@@ -945,7 +962,8 @@ const BlueprintSettings = (props) => {
scopeConnection?.providerId,
getJiraMappedBoards,
setConnections,
- boardSearch
+ boardSearch,
+ Providers.JIRA
])
useEffect(() => {
@@ -1037,7 +1055,7 @@ const BlueprintSettings = (props) => {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
{activeBlueprint?.id !== null && blueprintErrors.length === 0 && (
@@ -1257,6 +1275,7 @@ const BlueprintSettings = (props) => {
Data Scope and Transformation
</h2>
<DataScopesGrid
+ providers={Providers}
connections={connections}
blueprint={activeBlueprint}
onModify={modifyConnection}
@@ -1298,6 +1317,7 @@ const BlueprintSettings = (props) => {
</div>
</div>
<DataScopesGrid
+ providers={Providers}
connections={connections}
blueprint={activeBlueprint}
onModify={() => modifySetting('plan')}
diff --git a/config-ui/src/pages/blueprints/create-blueprint.jsx b/config-ui/src/pages/blueprints/create-blueprint.jsx
index 4a21cb63..13773464 100644
--- a/config-ui/src/pages/blueprints/create-blueprint.jsx
+++ b/config-ui/src/pages/blueprints/create-blueprint.jsx
@@ -31,9 +31,9 @@ import {
ISSUE_FIELDS_ENDPOINT,
BOARDS_ENDPOINT
} from '@/config/jiraApiProxy'
-import { integrationsData } from '@/data/integrations'
+// import { integrationsData } from '@/data/integrations'
import { Intent } from '@blueprintjs/core'
-import { Providers } from '@/data/Providers'
+// import { Providers } from '@/data/Providers'
import Nav from '@/components/Nav'
import Sidebar from '@/components/Sidebar'
// import AppCrumbs from '@/components/Breadcrumbs'
@@ -51,6 +51,7 @@ import {
DEFAULT_DATA_ENTITIES
} from '@/data/BlueprintWorkflow'
+import useIntegrations from '@/hooks/useIntegrations'
import useBlueprintManager from '@/hooks/useBlueprintManager'
import usePipelineManager from '@/hooks/usePipelineManager'
import useConnectionManager from '@/hooks/useConnectionManager'
@@ -91,6 +92,22 @@ const CreateBlueprint = (props) => {
const history = useHistory()
// const dispatch = useDispatch()
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ DataSources: DataSourcesList,
+ Providers,
+ ProviderLabels,
+ ProviderIcons,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderFormTooltips,
+ ProviderConnectionLimits,
+ setActiveProvider
+ } = useIntegrations()
+
const [blueprintAdvancedSteps, setBlueprintAdvancedSteps] = useState(
WorkflowAdvancedSteps
)
@@ -101,7 +118,9 @@ const CreateBlueprint = (props) => {
const [activeStep, setActiveStep] = useState(
blueprintSteps.find((s) => s.id === 1)
)
- const [activeProvider, setActiveProvider] = useState(integrationsData[0])
+
+ // @todo: Replace with Integrations Hook
+ // const [activeProvider, setActiveProvider] = useState(integrationsData[0])
const [enabledProviders, setEnabledProviders] = useState([])
const [runTasks, setRunTasks] = useState([])
@@ -118,6 +137,20 @@ const CreateBlueprint = (props) => {
NullBlueprintConnection
)
+ const ConnectionFormLabels = useMemo(
+ () => ProviderFormLabels[activeProvider?.id],
+ [ProviderFormLabels, activeProvider?.id]
+ )
+ const ConnectionFormPlaceholders = useMemo(
+ () => ProviderFormPlaceholders[activeProvider?.id],
+ [ProviderFormPlaceholders, activeProvider?.id]
+ )
+
+ const ConnectionFormTooltips = useMemo(
+ () => ProviderFormTooltips[activeProvider?.id],
+ [ProviderFormTooltips, activeProvider?.id]
+ )
+
const [dataEntitiesList, setDataEntitiesList] = useState([
...DEFAULT_DATA_ENTITIES
])
@@ -462,14 +495,20 @@ const CreateBlueprint = (props) => {
)
setActiveConnectionTab(tab)
setActiveProvider(
- integrationsData.find((p) => p.id === selectedConnection.provider)
+ Integrations.find((p) => p.id === selectedConnection.provider)
)
setProvider(
- integrationsData.find((p) => p.id === selectedConnection.provider)
+ Integrations.find((p) => p.id === selectedConnection.provider)
)
setConfiguredConnection(selectedConnection)
},
- [blueprintConnections, setProvider, setConfiguredConnection]
+ [
+ blueprintConnections,
+ setProvider,
+ setActiveProvider,
+ setConfiguredConnection,
+ Integrations
+ ]
)
const handleConnectionDialogOpen = useCallback(() => {
@@ -565,16 +604,16 @@ const CreateBlueprint = (props) => {
break
}
return items
- }, [dataEntitiesList, configuredConnection])
+ }, [dataEntitiesList, configuredConnection, Providers])
const manageConnection = useCallback(
(connection) => {
console.log('>> MANAGE CONNECTION...', connection)
if (connection?.id !== null) {
setActiveProvider(
- integrationsData.find((p) => p.id === connection.provider)
+ Integrations.find((p) => p.id === connection.provider)
)
- setProvider(integrationsData.find((p) => p.id === connection.provider))
+ setProvider(Integrations.find((p) => p.id === connection.provider))
setManagedConnection(connection)
setConnectionDialogIsOpen(true)
}
@@ -583,7 +622,8 @@ const CreateBlueprint = (props) => {
setProvider,
setActiveProvider,
setManagedConnection,
- setConnectionDialogIsOpen
+ setConnectionDialogIsOpen,
+ Integrations
]
)
@@ -683,7 +723,8 @@ const CreateBlueprint = (props) => {
fetchFields,
fetchIssueTypes,
enabledProviders,
- mode
+ mode,
+ Providers.JIRA
])
useEffect(() => {
@@ -757,31 +798,10 @@ const CreateBlueprint = (props) => {
setConfiguredConnection(someConnection)
setActiveConnectionTab(`connection-${someConnection?.id}`)
setActiveProvider(
- integrationsData.find((p) => p.id === someConnection.provider)
- )
- setProvider(
- integrationsData.find((p) => p.id === someConnection.provider)
+ Integrations.find((p) => p.id === someConnection.provider)
)
+ setProvider(Integrations.find((p) => p.id === someConnection.provider))
}
- // const getDefaultEntities = (providerId) => {
- // let entities = []
- // switch (providerId) {
- // case Providers.GITHUB:
- // case Providers.GITLAB:
- // entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name !== 'ci-cd')
- // break
- // case Providers.JIRA:
- // entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'issue-tracking' || d.name === 'cross-domain')
- // break
- // case Providers.JENKINS:
- // entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'ci-cd')
- // break
- // case Providers.TAPD:
- // entities = DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'ci-cd')
- // break
- // }
- // return entities
- // }
const initializeEntities = (pV, cV) => ({
...pV,
[cV.id]: !pV[cV.id] ? getDefaultEntities(cV?.provider) : []
@@ -807,10 +827,12 @@ const CreateBlueprint = (props) => {
getDefaultEntities,
setConfiguredConnection,
setProvider,
+ setActiveProvider,
setBoards,
setDataEntities,
setProjects,
- testSelectedConnections
+ testSelectedConnections,
+ Integrations
])
useEffect(() => {
@@ -818,35 +840,18 @@ const CreateBlueprint = (props) => {
if (configuredConnection) {
setConfiguredProject(null)
setConfiguredBoard(null)
- switch (configuredConnection.provider) {
- case Providers.GITLAB:
- case Providers.GITHUB:
- setDataEntitiesList(
- DEFAULT_DATA_ENTITIES.filter((d) => d.name !== 'ci-cd')
- )
- break
- case Providers.JIRA:
- setDataEntitiesList(
- DEFAULT_DATA_ENTITIES.filter(
- (d) => d.name === 'issue-tracking' || d.name === 'cross-domain'
- )
- )
- break
- case Providers.JENKINS:
- setDataEntitiesList(
- DEFAULT_DATA_ENTITIES.filter((d) => d.name === 'ci-cd')
- )
- break
- default:
- setDataEntitiesList(DEFAULT_DATA_ENTITIES)
- break
- }
+ const plugin = Integrations.find(
+ (p) => p.id === configuredConnection?.provider
+ )
+ setDataEntitiesList((dL) => (plugin ? plugin?.getDataEntities() : dL))
}
}, [
configuredConnection,
setActiveConnectionTab,
setConfiguredBoard,
- setConfiguredProject
+ setConfiguredProject,
+ Providers,
+ Integrations
])
useEffect(() => {
@@ -1004,7 +1009,7 @@ const CreateBlueprint = (props) => {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<WorkflowStepsBar activeStep={activeStep} steps={blueprintSteps} />
@@ -1217,7 +1222,8 @@ const CreateBlueprint = (props) => {
</div>
<ConnectionDialog
- integrations={integrationsData}
+ integrations={Integrations}
+ dataSourcesList={DataSourcesList}
activeProvider={activeProvider}
setProvider={setActiveProvider}
setTestStatus={setTestStatus}
@@ -1252,6 +1258,9 @@ const CreateBlueprint = (props) => {
testStatus={testStatus}
testResponse={testResponse}
allTestResponses={allTestResponses}
+ labels={ConnectionFormLabels}
+ placeholders={ConnectionFormPlaceholders}
+ tooltips={ConnectionFormTooltips}
/>
<CodeInspector
diff --git a/config-ui/src/pages/blueprints/index.jsx b/config-ui/src/pages/blueprints/index.jsx
index f059a6f1..3beb030c 100644
--- a/config-ui/src/pages/blueprints/index.jsx
+++ b/config-ui/src/pages/blueprints/index.jsx
@@ -31,6 +31,7 @@ import {
NonIdealState,
Elevation
} from '@blueprintjs/core'
+import useIntegrations from '@/hooks/useIntegrations'
import usePipelineManager from '@/hooks/usePipelineManager'
import useBlueprintManager from '@/hooks/useBlueprintManager'
import useBlueprintValidation from '@/hooks/useBlueprintValidation'
@@ -39,13 +40,21 @@ import Nav from '@/components/Nav'
import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
import Content from '@/components/Content'
-import AddBlueprintDialog from '@/components/blueprints/AddBlueprintDialog'
+// import AddBlueprintDialog from '@/components/blueprints/AddBlueprintDialog'
import { ReactComponent as NoBlueprintsIcon } from '@/images/no-blueprints.svg'
import BlueprintsGrid from '@/components/blueprints/BlueprintsGrid'
const Blueprints = (props) => {
const history = useHistory()
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ setActiveProvider
+ } = useIntegrations()
+
const {
// eslint-disable-next-line no-unused-vars
blueprint,
@@ -217,31 +226,31 @@ const Blueprints = (props) => {
console.log('>>> ACTIVE/EXPANDED BLUEPRINT', activeBlueprint)
}, [activeBlueprint, getSchedule, pipelines])
- useEffect(() => {
- if (draftBlueprint && draftBlueprint.id) {
- console.log('>>> DRAFT = ', draftBlueprint)
- setBlueprintName(draftBlueprint.name)
- setCronConfig(
- !isStandardCronPreset(draftBlueprint.cronConfig)
- ? 'custom'
- : draftBlueprint.cronConfig
- )
- setCustomCronConfig(draftBlueprint.cronConfig)
- setBlueprintTasks(draftBlueprint.tasks)
- setEnableBlueprint(draftBlueprint.enable)
- setDetectedProviderTasks(draftBlueprint.tasks.flat())
- setBlueprintDialogIsOpen(true)
- }
- }, [
- draftBlueprint,
- setBlueprintName,
- setCronConfig,
- isStandardCronPreset,
- setBlueprintTasks,
- setEnableBlueprint,
- setCustomCronConfig,
- setDetectedProviderTasks
- ])
+ // useEffect(() => {
+ // if (draftBlueprint && draftBlueprint.id) {
+ // console.log('>>> DRAFT = ', draftBlueprint)
+ // setBlueprintName(draftBlueprint.name)
+ // setCronConfig(
+ // !isStandardCronPreset(draftBlueprint.cronConfig)
+ // ? 'custom'
+ // : draftBlueprint.cronConfig
+ // )
+ // setCustomCronConfig(draftBlueprint.cronConfig)
+ // setBlueprintTasks(draftBlueprint.tasks)
+ // setEnableBlueprint(draftBlueprint.enable)
+ // setDetectedProviderTasks(draftBlueprint.tasks.flat())
+ // setBlueprintDialogIsOpen(true)
+ // }
+ // }, [
+ // draftBlueprint,
+ // setBlueprintName,
+ // setCronConfig,
+ // isStandardCronPreset,
+ // setBlueprintTasks,
+ // setEnableBlueprint,
+ // setCustomCronConfig,
+ // setDetectedProviderTasks
+ // ])
useEffect(() => {
if (saveComplete?.id) {
@@ -340,7 +349,7 @@ const Blueprints = (props) => {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
{/* <AppCrumbs
@@ -462,7 +471,7 @@ const Blueprints = (props) => {
</Content>
</div>
- <AddBlueprintDialog
+ {/* <AddBlueprintDialog
isLoading={isFetchingAllPipelines}
isOpen={blueprintDialogIsOpen}
setIsOpen={setBlueprintDialogIsOpen}
@@ -490,7 +499,7 @@ const Blueprints = (props) => {
detectedProviders={detectedProviderTasks}
getCronPreset={getCronPreset}
getCronPresetByConfig={getCronPresetByConfig}
- />
+ /> */}
</>
)
}
diff --git a/config-ui/src/pages/configure/connections/AddConnection.jsx b/config-ui/src/pages/configure/connections/AddConnection.jsx
index 9242ac78..93603f14 100644
--- a/config-ui/src/pages/configure/connections/AddConnection.jsx
+++ b/config-ui/src/pages/configure/connections/AddConnection.jsx
@@ -23,15 +23,16 @@ import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
import Content from '@/components/Content'
import ConnectionForm from '@/pages/configure/connections/ConnectionForm'
-import { integrationsData } from '@/data/integrations'
-import {
- ProviderConnectionLimits,
- ProviderFormLabels,
- ProviderFormPlaceholders,
- ProviderLabels,
- Providers
-} from '@/data/Providers'
+// import { integrationsData } from '@/data/integrations'
+// import {
+// ProviderConnectionLimits,
+// ProviderFormLabels,
+// ProviderFormPlaceholders,
+// ProviderLabels,
+// Providers
+// } from '@/data/Providers'
+import useIntegrations from '@/hooks/useIntegrations'
import useConnectionManager from '@/hooks/useConnectionManager'
import useConnectionValidation from '@/hooks/useConnectionValidation'
@@ -42,9 +43,23 @@ export default function AddConnection() {
const history = useHistory()
const { providerId } = useParams()
- const [activeProvider, setActiveProvider] = useState(
- integrationsData.find((p) => p.id === providerId)
- )
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ Providers,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderFormTooltips,
+ ProviderConnectionLimits,
+ setActiveProvider
+ } = useIntegrations()
+
+ // @todo: Replace with Integrations Hook
+ // const [activeProvider, setActiveProvider] = useState(
+ // integrationsData.find((p) => p.id === providerId)
+ // )
const {
testConnection,
@@ -95,7 +110,7 @@ export default function AddConnection() {
})
const cancel = () => {
- history.push(`/integrations/${activeProvider.id}`)
+ history.push(`/integrations/${activeProvider?.id}`)
}
// const resetForm = () => {
@@ -107,34 +122,35 @@ export default function AddConnection() {
// }
useEffect(() => {
+ // @todo: Cleanup Restricted Provider Names (Legacy Feature)
// Selected Provider
- if (activeProvider?.id) {
- fetchAllConnections()
- switch (activeProvider.id) {
- case Providers.JENKINS:
- // setName(ProviderLabels.JENKINS)
- break
- case Providers.GITHUB:
- case Providers.GITLAB:
- case Providers.JIRA:
- case Providers.TAPD:
- default:
- setName('')
- break
- }
- }
- }, [activeProvider.id, fetchAllConnections, setName])
+ // if (activeProvider?.id) {
+ // fetchAllConnections()
+ // switch (activeProvider?.id) {
+ // case Providers.JENKINS:
+ // // setName(ProviderLabels.JENKINS)
+ // break
+ // case Providers.GITHUB:
+ // case Providers.GITLAB:
+ // case Providers.JIRA:
+ // case Providers.TAPD:
+ // default:
+ // setName('')
+ // break
+ // }
+ // }
+ }, [activeProvider?.id, fetchAllConnections, setName])
useEffect(() => {
console.log('>>>> DETECTED PROVIDER = ', providerId)
- setActiveProvider(integrationsData.find((p) => p.id === providerId))
- }, [providerId])
+ setActiveProvider(Integrations.find((p) => p.id === providerId))
+ }, [providerId, setActiveProvider, Integrations])
return (
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<AppCrumbs
@@ -142,12 +158,12 @@ export default function AddConnection() {
{ href: '/', icon: false, text: 'Dashboard' },
{ href: '/integrations', icon: false, text: 'Connections' },
{
- href: `/integrations/${activeProvider.id}`,
+ href: `/integrations/${activeProvider?.id}`,
icon: false,
- text: `${activeProvider.name}`
+ text: `${activeProvider?.name}`
},
{
- href: `/connections/add/${activeProvider.id}`,
+ href: `/connections/add/${activeProvider?.id}`,
icon: false,
text: 'Add Connection',
current: true
@@ -157,19 +173,25 @@ export default function AddConnection() {
<div style={{ width: '100%' }}>
<Link
style={{ float: 'right', marginLeft: '10px', color: '#777777' }}
- to={`/integrations/${activeProvider.id}`}
+ to={`/integrations/${activeProvider?.id}`}
>
<Icon icon='undo' size={16} /> Go Back
</Link>
<div style={{ display: 'flex' }}>
<div>
<span style={{ marginRight: '10px' }}>
- {activeProvider.icon}
+ <img
+ className='providerIconSvg'
+ src={'/' + activeProvider?.icon}
+ width={40}
+ height={40}
+ style={{ width: '40px', height: '40px' }}
+ />
</span>
</div>
<div>
<h1 style={{ margin: 0 }}>
- {activeProvider.name} Add Connection
+ {activeProvider?.name} Add Connection
</h1>
<p className='page-description'>
Create a new connection for this provider.
@@ -208,18 +230,11 @@ export default function AddConnection() {
allTestResponses={allTestResponses}
errors={errors}
showError={showError}
- authType={
- [
- Providers.JENKINS,
- Providers.JIRA,
- Providers.TAPD
- ].includes(activeProvider.id)
- ? 'plain'
- : 'token'
- }
+ authType={activeProvider?.getAuthenticationType()}
sourceLimits={ProviderConnectionLimits}
- labels={ProviderFormLabels[activeProvider.id]}
- placeholders={ProviderFormPlaceholders[activeProvider.id]}
+ labels={ProviderFormLabels[activeProvider?.id]}
+ placeholders={ProviderFormPlaceholders[activeProvider?.id]}
+ tooltips={ProviderFormTooltips[activeProvider?.id]}
/>
</div>
{/* {validationErrors.length > 0 && (
diff --git a/config-ui/src/pages/configure/connections/ConfigureConnection.jsx b/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
index 434af61c..8baccbf8 100644
--- a/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
+++ b/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
@@ -23,21 +23,24 @@ import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
import Content from '@/components/Content'
import ContentLoader from '@/components/loaders/ContentLoader'
+import useIntegrations from '@/hooks/useIntegrations'
import useConnectionManager from '@/hooks/useConnectionManager'
-import useSettingsManager from '@/hooks/useSettingsManager'
+// import useSettingsManager from '@/hooks/useSettingsManager'
import useConnectionValidation from '@/hooks/useConnectionValidation'
import ConnectionForm from '@/pages/configure/connections/ConnectionForm'
import DeleteAction from '@/components/actions/DeleteAction'
import DeleteConfirmationMessage from '@/components/actions/DeleteConfirmationMessage'
-import { integrationsData } from '@/data/integrations'
+// @todo: Replace with Integrations Hook
+// import { integrationsData } from '@/data/integrations'
import { NullSettings } from '@/data/NullSettings'
-import {
- ProviderConnectionLimits,
- ProviderFormLabels,
- ProviderFormPlaceholders,
- Providers
-} from '@/data/Providers'
+// @todo: Replace with Integrations Hook
+// import {
+// ProviderConnectionLimits,
+// ProviderFormLabels,
+// ProviderFormPlaceholders,
+// Providers
+// } from '@/data/Providers'
import '@/styles/integration.scss'
import '@/styles/connections.scss'
@@ -47,9 +50,23 @@ export default function ConfigureConnection() {
const history = useHistory()
const { providerId, connectionId } = useParams()
- const [activeProvider, setActiveProvider] = useState(
- integrationsData.find((p) => p.id === providerId)
- )
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ Providers,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderFormTooltips,
+ ProviderConnectionLimits,
+ setActiveProvider
+ } = useIntegrations()
+
+ // @todo: Replace with Integrations Hook
+ // const [activeProvider, setActiveProvider] = useState(
+ // integrationsData.find((p) => p.id === providerId)
+ // )
// const [activeConnection, setActiveConnection] = useState(NullConnection)
const [showConnectionSettings, setShowConnectionSettings] = useState(true)
const [deleteId, setDeleteId] = useState(null)
@@ -97,17 +114,17 @@ export default function ConfigureConnection() {
true
)
- const {
- saveSettings,
- // errors: settingsErrors,
- isSaving
- // isTesting,
- // showError,
- } = useSettingsManager({
- activeProvider,
- activeConnection,
- settings
- })
+ // const {
+ // saveSettings,
+ // // errors: settingsErrors,
+ // isSaving
+ // // isTesting,
+ // // showError,
+ // } = useSettingsManager({
+ // activeProvider,
+ // activeConnection,
+ // settings
+ // })
const {
validate,
@@ -125,42 +142,43 @@ export default function ConfigureConnection() {
})
const cancel = () => {
- history.push(`/integrations/${activeProvider.id}`)
+ history.push(`/integrations/${activeProvider?.id}`)
}
- const renderProviderSettings = useCallback(
- (providerId, activeProvider) => {
- console.log('>>> RENDERING PROVIDER SETTINGS...')
- let settingsComponent = null
- if (activeProvider && activeProvider.settings) {
- settingsComponent = activeProvider.settings({
- activeProvider,
- activeConnection,
- isSaving,
- isSavingConnection,
- setSettings
- })
- } else {
- // @todo create & display "fallback/empty settings" view
- console.log(
- '>> WARNING: NO PROVIDER SETTINGS RENDERED, PROVIDER = ',
- activeProvider
- )
- }
- return settingsComponent
- },
- [activeConnection, isSaving, isSavingConnection]
- )
+ // @todo: cleanup unused render helper
+ // const renderProviderSettings = useCallback(
+ // (providerId, activeProvider) => {
+ // console.log('>>> RENDERING PROVIDER SETTINGS...')
+ // let settingsComponent = null
+ // if (activeProvider && activeProvider.settings) {
+ // settingsComponent = activeProvider.settings({
+ // activeProvider,
+ // activeConnection,
+ // isSaving,
+ // isSavingConnection,
+ // setSettings
+ // })
+ // } else {
+ // // @todo create & display "fallback/empty settings" view
+ // console.log(
+ // '>> WARNING: NO PROVIDER SETTINGS RENDERED, PROVIDER = ',
+ // activeProvider
+ // )
+ // }
+ // return settingsComponent
+ // },
+ // [activeConnection, isSaving, isSavingConnection]
+ // )
useEffect(() => {
console.log('>>>> DETECTED PROVIDER ID = ', providerId)
console.log('>>>> DETECTED CONNECTION ID = ', connectionId)
if (connectionId && providerId) {
- setActiveProvider(integrationsData.find((p) => p.id === providerId))
+ setActiveProvider(Integrations.find((p) => p.id === providerId))
} else {
console.log('NO PARAMS!')
}
- }, [connectionId, providerId])
+ }, [connectionId, providerId, Integrations, setActiveProvider])
useEffect(() => {}, [settings])
@@ -179,7 +197,7 @@ export default function ConfigureConnection() {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<AppCrumbs
@@ -187,12 +205,12 @@ export default function ConfigureConnection() {
{ href: '/', icon: false, text: 'Dashboard' },
{ href: '/integrations', icon: false, text: 'Connections' },
{
- href: `/integrations/${activeProvider.id}`,
+ href: `/integrations/${activeProvider?.id}`,
icon: false,
- text: `${activeProvider.name}`
+ text: `${activeProvider?.name}`
},
{
- href: `/connections/configure/${activeProvider.id}/${activeConnection?.id}`,
+ href: `/connections/configure/${activeProvider?.id}/${activeConnection?.id}`,
icon: false,
text: `${
activeConnection ? activeConnection.name : 'Configure'
@@ -209,7 +227,7 @@ export default function ConfigureConnection() {
marginLeft: '10px',
color: '#777777'
}}
- to={`/integrations/${activeProvider.id}`}
+ to={`/integrations/${activeProvider?.id}`}
>
<Icon icon='fast-backward' size={16} /> Connection List
</Link>
@@ -217,7 +235,13 @@ export default function ConfigureConnection() {
<div style={{ display: 'flex', justifyContent: 'flex-start' }}>
<div>
<span style={{ marginRight: '10px' }}>
- {activeProvider.icon}
+ <img
+ className='providerIconSvg'
+ src={'/' + activeProvider?.icon}
+ width={40}
+ height={40}
+ style={{ width: '40px', height: '40px' }}
+ />
</span>
</div>
{isLoadingConnection && (
@@ -232,11 +256,11 @@ export default function ConfigureConnection() {
<h1 style={{ margin: 0 }}>
Manage{' '}
<strong style={{ fontWeight: 900 }}>
- {activeProvider.name}
+ {activeProvider?.name}
</strong>{' '}
Settings
</h1>
- {activeProvider.multiConnection && (
+ {activeProvider?.multiConnection && (
<div style={{ paddingTop: '5px' }}>
<DeleteAction
id={deleteId}
@@ -263,7 +287,7 @@ export default function ConfigureConnection() {
Providers.GITLAB,
Providers.JIRA,
Providers.TAPD
- ].includes(activeProvider.id) && (
+ ].includes(activeProvider?.id) && (
<h2 style={{ margin: 0 }}>
#{activeConnection?.id} {activeConnection.name}
</h2>
@@ -337,21 +361,14 @@ export default function ConfigureConnection() {
allTestResponses={allTestResponses}
errors={errors}
showError={showConnectionError}
- authType={
- [
- Providers.JENKINS,
- Providers.JIRA,
- Providers.TAPD
- ].includes(activeProvider.id)
- ? 'plain'
- : 'token'
- }
+ authType={activeProvider?.getAuthenticationType()}
showLimitWarning={false}
sourceLimits={ProviderConnectionLimits}
- labels={ProviderFormLabels[activeProvider.id]}
+ labels={ProviderFormLabels[activeProvider?.id]}
placeholders={
- ProviderFormPlaceholders[activeProvider.id]
+ ProviderFormPlaceholders[activeProvider?.id]
}
+ tooltips={ProviderFormTooltips[activeProvider?.id]}
/>
</div>
) : (
diff --git a/config-ui/src/pages/configure/connections/ConnectionForm.jsx b/config-ui/src/pages/configure/connections/ConnectionForm.jsx
index 27141721..deb7eab0 100644
--- a/config-ui/src/pages/configure/connections/ConnectionForm.jsx
+++ b/config-ui/src/pages/configure/connections/ConnectionForm.jsx
@@ -15,7 +15,14 @@
* limitations under the License.
*
*/
-import React, { useEffect, useState, useCallback, useRef, useMemo } from 'react'
+import React, {
+ useEffect,
+ useState,
+ useCallback,
+ useRef,
+ useMemo,
+ useContext
+} from 'react'
import {
Button,
Colors,
@@ -32,15 +39,33 @@ import {
Intent,
PopoverInteractionKind,
NumericInput,
- Switch
+ Switch,
+ Tooltip
} from '@blueprintjs/core'
-import { Providers } from '@/data/Providers'
+import IntegrationsContext from '@/store/integrations-context'
+// import { Providers } from '@/data/Providers'
import FormValidationErrors from '@/components/messages/FormValidationErrors'
import InputValidationError from '@/components/validation/InputValidationError'
import '@/styles/integration.scss'
import '@/styles/connections.scss'
+const TooltipIcon = (props) => (
+ <Icon
+ icon='info-sign'
+ size={12}
+ style={{
+ float: 'left',
+ display: 'inline-block',
+ alignContent: 'center',
+ marginBottom: '4px',
+ marginLeft: '8px',
+ color: '#999'
+ }}
+ {...props}
+ />
+)
+
export default function ConnectionForm(props) {
const {
isLocked = false,
@@ -80,13 +105,16 @@ export default function ConnectionForm(props) {
authType = 'token',
sourceLimits = {},
showLimitWarning = true,
- labels,
- placeholders,
+ labels = {},
+ placeholders = {},
+ tooltips = {},
enableActions = true,
formGroupClassName = 'formGroup',
showHeadline = true
} = props
+ const { Providers } = useContext(IntegrationsContext)
+
const connectionNameRef = useRef()
const connectionEndpointRef = useRef()
const connectionTokenRef = useRef()
@@ -240,7 +268,12 @@ export default function ConnectionForm(props) {
console.log('>> PERSONAL ACCESS TOKENS ENTERED...', personalAccessTokens)
syncPersonalAcessTokens()
}
- }, [activeProvider?.id, personalAccessTokens, syncPersonalAcessTokens])
+ }, [
+ activeProvider?.id,
+ personalAccessTokens,
+ syncPersonalAcessTokens,
+ Providers.GITHUB
+ ])
useEffect(() => {
console.log(
@@ -435,6 +468,15 @@ export default function ConnectionForm(props) {
>
<Label>
{labels ? labels.token : <>Basic Auth Token</>}
+ {tooltips?.token ? (
+ <Tooltip
+ className='connection-tooltip'
+ intent={Intent.PRIMARY}
+ content={tooltips?.token}
+ >
+ <TooltipIcon />
+ </Tooltip>
+ ) : null}
<span className='requiredStar'>*</span>
</Label>
{[Providers.GITHUB].includes(activeProvider?.id) ? (
@@ -716,6 +758,15 @@ export default function ConnectionForm(props) {
>
<Label>
{labels ? labels.password : <>Password</>}
+ {tooltips?.password ? (
+ <Tooltip
+ className='connection-tooltip'
+ intent={Intent.PRIMARY}
+ content={tooltips?.password}
+ >
+ <TooltipIcon />
+ </Tooltip>
+ ) : null}
<span className='requiredStar'>*</span>
</Label>
<InputGroup
@@ -797,6 +848,15 @@ export default function ConnectionForm(props) {
>
<Label>
{labels ? labels.rateLimitPerHour : <>Rate Limit</>}
+ {tooltips?.rateLimitPerHour ? (
+ <Tooltip
+ className='connection-tooltip'
+ intent={Intent.PRIMARY}
+ content={tooltips?.rateLimitPerHour}
+ >
+ <TooltipIcon />
+ </Tooltip>
+ ) : null}
</Label>
<div
className='ratelimit-options'
diff --git a/config-ui/src/pages/configure/integration/index.jsx b/config-ui/src/pages/configure/integration/index.jsx
index 80ee4d96..ef3ce932 100644
--- a/config-ui/src/pages/configure/integration/index.jsx
+++ b/config-ui/src/pages/configure/integration/index.jsx
@@ -17,11 +17,14 @@
*/
import React, { useEffect, useState } from 'react'
import { useHistory } from 'react-router-dom'
+import { Colors, Icon } from '@blueprintjs/core'
import Nav from '@/components/Nav'
import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
import Content from '@/components/Content'
-import { integrationsData } from '@/data/integrations'
+// @todo: replace with Integrations Hook
+// import { integrationsData } from '@/data/integrations'
+import useIntegrations from '@/hooks/useIntegrations'
import { ReactComponent as WebHookProviderIcon } from '@/images/integrations/incoming-webhook.svg'
import '@/styles/integration.scss'
@@ -29,30 +32,37 @@ import '@/styles/integration.scss'
export default function Integration() {
const history = useHistory()
- const [activeProvider, setActiveProvider] = useState(integrationsData[0])
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ setActiveProvider
+ } = useIntegrations()
+ // const [activeProvider, setActiveProvider] = useState(integrationsData[0])
const handleProviderClick = (providerId) => {
- const theProvider = integrationsData.find((p) => p.id === providerId)
+ const theProvider = Plugins.find((p) => p.id === providerId)
if (theProvider) {
setActiveProvider(theProvider)
history.push(`/integrations/${theProvider.id}`)
} else {
- setActiveProvider(integrationsData[0])
+ setActiveProvider(Plugins.find[0])
}
}
- useEffect(() => {
- // Selected Provider
- console.log(activeProvider)
- }, [activeProvider, history])
+ // useEffect(() => {
+ // // Selected Provider
+ // console.log(activeProvider)
+ // }, [activeProvider, history])
- useEffect(() => {}, [])
+ // useEffect(() => {}, [])
return (
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<AppCrumbs
@@ -69,18 +79,50 @@ export default function Integration() {
<div className='headlineContainer'>
<h1>Data Connections</h1>
<p className='page-description'>
- {integrationsData.length} connections are available for data
+ {Integrations.length} connections are available for data
collection.
</p>
</div>
<div className='integrationProviders'>
- {integrationsData.map((provider) => (
+ {Integrations.map((provider) => (
<div
className='iProvider'
key={`provider-${provider.id}`}
onClick={() => handleProviderClick(provider.id)}
+ style={{ position: 'relative' }}
>
- <div className='providerIcon'>{provider.iconDashboard}</div>
+ {provider?.private && (
+ <span
+ style={{
+ width: '20px',
+ height: '20px',
+ position: 'absolute',
+ top: '-5px',
+ right: '-5px',
+ textAlign: 'center',
+ lineHeight: '16px',
+ backgroundColor: '#fff',
+ display: 'block',
+ borderRadius: '50%',
+ border: '1px solid #eee'
+ }}
+ >
+ <Icon
+ icon='lock'
+ size={10}
+ style={{ color: Colors.RED5 }}
+ />
+ </span>
+ )}
+ <div className='providerIcon'>
+ <img
+ className='providerIconSvg'
+ src={provider.icon}
+ width={40}
+ height={40}
+ style={{ width: '40px', height: '40px' }}
+ />
+ </div>
<div className='providerName'>
{provider.name}{' '}
{provider.isBeta && (
diff --git a/config-ui/src/pages/configure/integration/manage.jsx b/config-ui/src/pages/configure/integration/manage.jsx
index aca63b6d..84379cc8 100644
--- a/config-ui/src/pages/configure/integration/manage.jsx
+++ b/config-ui/src/pages/configure/integration/manage.jsx
@@ -33,9 +33,10 @@ import Sidebar from '@/components/Sidebar'
import AppCrumbs from '@/components/Breadcrumbs'
import Content from '@/components/Content'
import { ToastNotification } from '@/components/Toast'
+import useIntegrations from '@/hooks/useIntegrations'
import useConnectionManager from '@/hooks/useConnectionManager'
-import { integrationsData } from '@/data/integrations'
+// import { integrationsData } from '@/data/integrations'
import DeleteAction from '@/components/actions/DeleteAction'
import DeleteConfirmationMessage from '@/components/actions/DeleteConfirmationMessage'
import ContentLoader from '@/components/loaders/ContentLoader'
@@ -47,10 +48,18 @@ export default function ManageIntegration() {
const { providerId } = useParams()
- const [integrations, setIntegrations] = useState(integrationsData)
- const [activeProvider, setActiveProvider] = useState(
- integrations.find((p) => p.id === providerId)
- )
+ const {
+ registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ setActiveProvider
+ } = useIntegrations()
+
+ // const [integrations, setIntegrations] = useState(integrationsData)
+ // const [activeProvider, setActiveProvider] = useState(
+ // integrations.find((p) => p.id === providerId)
+ // )
const [isRunningDelete, setIsRunningDelete] = useState(false)
const [deleteId, setDeleteId] = useState(null)
@@ -65,13 +74,14 @@ export default function ManageIntegration() {
fetchAllConnections,
errors,
deleteComplete,
- testAllConnections
+ testAllConnections,
+ setProvider: setConnectionProvider
} = useConnectionManager({
activeProvider
})
const addConnection = () => {
- history.push(`/connections/add/${activeProvider.id}`)
+ history.push(`/connections/add/${activeProvider?.id}`)
}
const editConnection = (connection, e) => {
@@ -81,13 +91,13 @@ export default function ManageIntegration() {
(!e.target.classList.contains('cell-actions') ||
!e.target.classList.contains('actions-link'))
) {
- history.push(`/connections/edit/${activeProvider.id}/${connection.id}`)
+ history.push(`/connections/edit/${activeProvider?.id}/${connection.id}`)
}
}
const configureConnection = (connection) => {
const { id, ID, endpoint } = connection
- history.push(`/connections/configure/${activeProvider.id}/${id || ID}`)
+ history.push(`/connections/configure/${activeProvider?.id}/${id || ID}`)
console.log('>> editing/modifying connection: ', id, endpoint)
}
@@ -140,14 +150,16 @@ export default function ManageIntegration() {
}
useEffect(() => {
- fetchAllConnections(false)
+ if (activeProvider) {
+ fetchAllConnections(false)
+ }
}, [activeProvider, fetchAllConnections])
useEffect(() => {
console.log('>> ACTIVE PROVIDER = ', providerId)
- setIntegrations(integrations)
- setActiveProvider(integrations.find((p) => p.id === providerId))
- }, [integrations, providerId])
+ setActiveProvider(Integrations.find((p) => p.id === providerId))
+ setConnectionProvider(Integrations.find((p) => p.id === providerId))
+ }, [Integrations, providerId, setActiveProvider, setConnectionProvider])
useEffect(() => {
let flushTimeout
@@ -171,7 +183,7 @@ export default function ManageIntegration() {
<>
<div className='container'>
<Nav />
- <Sidebar />
+ <Sidebar key={Integrations} integrations={Integrations} />
<Content>
<main className='main'>
<AppCrumbs
@@ -179,9 +191,9 @@ export default function ManageIntegration() {
{ href: '/', icon: false, text: 'Dashboard' },
{ href: '/integrations', icon: false, text: 'Connections' },
{
- href: `/integrations/${activeProvider.id}`,
+ href: `/integrations/${activeProvider?.id}`,
icon: false,
- text: `${activeProvider.name}`,
+ text: `${activeProvider?.name}`,
current: true
}
]}
@@ -196,13 +208,19 @@ export default function ManageIntegration() {
<div style={{ display: 'flex' }}>
<div>
<span style={{ marginRight: '10px' }}>
- {activeProvider.icon}
+ <img
+ className='providerIconSvg'
+ src={'/' + activeProvider?.icon}
+ width={40}
+ height={40}
+ style={{ width: '40px', height: '40px' }}
+ />
</span>
</div>
<div>
<h1 style={{ margin: 0 }}>
- {activeProvider.name} Connections{' '}
- {activeProvider.isBeta && (
+ {activeProvider?.name} Connections{' '}
+ {activeProvider?.isBeta && (
<>
<sup>(beta)</sup>
</>
@@ -282,6 +300,7 @@ export default function ManageIntegration() {
</p>
<p>
<Button
+ disabled={connectionLimitReached}
onClick={addConnection}
rightIcon='add'
intent='primary'
@@ -461,7 +480,7 @@ export default function ManageIntegration() {
))}
</tbody>
</table>
- {connectionLimitReached && (
+ {!!connectionLimitReached && (
<p
style={{
margin: 0,
@@ -490,7 +509,7 @@ export default function ManageIntegration() {
}}
>
Fetched <strong>{connections.length}</strong> connection(s)
- from Lake API for <strong>{activeProvider.name}</strong>
+ from Lake API for <strong>{activeProvider?.name}</strong>
</p>
</>
)}
diff --git a/config-ui/src/pages/configure/settings/github.jsx b/config-ui/src/pages/configure/settings/github.jsx
index b9ef32d9..9ab9a766 100644
--- a/config-ui/src/pages/configure/settings/github.jsx
+++ b/config-ui/src/pages/configure/settings/github.jsx
@@ -37,6 +37,8 @@ import '@/styles/connections.scss'
export default function GithubSettings(props) {
const {
+ Providers,
+ ProviderLabels,
provider,
connection,
entities = [],
diff --git a/config-ui/src/pages/configure/settings/jira.jsx b/config-ui/src/pages/configure/settings/jira.jsx
index 8c397802..491963db 100644
--- a/config-ui/src/pages/configure/settings/jira.jsx
+++ b/config-ui/src/pages/configure/settings/jira.jsx
@@ -53,6 +53,8 @@ const createTypeMapObject = (customType, standardType) => {
export default function JiraSettings(props) {
const {
+ Providers,
+ ProviderLabels,
provider,
connection,
issueTypes = [],
diff --git a/config-ui/src/pages/configure/settings/tapd.jsx b/config-ui/src/pages/configure/settings/tapd.jsx
index 3d472a98..a98b9af1 100644
--- a/config-ui/src/pages/configure/settings/tapd.jsx
+++ b/config-ui/src/pages/configure/settings/tapd.jsx
@@ -24,6 +24,8 @@ import '@/styles/connections.scss'
export default function TapdSettings(props) {
const {
+ Providers,
+ ProviderLabels,
provider,
connection,
entities = [],
diff --git a/config-ui/src/registry/plugins/ae.json b/config-ui/src/registry/plugins/ae.json
new file mode 100644
index 00000000..ec9b770e
--- /dev/null
+++ b/config-ui/src/registry/plugins/ae.json
@@ -0,0 +1,13 @@
+{
+ "id": "ae",
+ "name": "AE",
+ "type": "plugin",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/ae.svg",
+ "private": false,
+ "connection": null,
+ "entities": []
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/azure.json b/config-ui/src/registry/plugins/azure.json
new file mode 100644
index 00000000..8e796718
--- /dev/null
+++ b/config-ui/src/registry/plugins/azure.json
@@ -0,0 +1,53 @@
+{
+ "id": "azure",
+ "name": "Azure CI",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/azure.svg",
+ "private": false,
+ "connection": {
+ "authentication": "plain",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "username": { },
+ "password": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "username": "Username",
+ "password": "Password",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. Azure CI",
+ "endpoint": "URL eg. https://api.azure.com/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CICD"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ }
+ },
+ "default": {
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/bitbucket.json b/config-ui/src/registry/plugins/bitbucket.json
new file mode 100644
index 00000000..9f159a56
--- /dev/null
+++ b/config-ui/src/registry/plugins/bitbucket.json
@@ -0,0 +1,53 @@
+{
+ "id": "bitbucket",
+ "name": "Bitbucket",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/bitbucket.svg",
+ "private": false,
+ "connection": {
+ "authentication": "plain",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "username": { },
+ "password": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "username": "Username",
+ "password": "Password",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. Bitbucket",
+ "endpoint": "URL eg. https://api.bitbucket.io/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CICD"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ }
+ },
+ "default": {
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/dbt.json b/config-ui/src/registry/plugins/dbt.json
new file mode 100644
index 00000000..900b7446
--- /dev/null
+++ b/config-ui/src/registry/plugins/dbt.json
@@ -0,0 +1,12 @@
+{
+ "id": "dbt",
+ "name": "DBT",
+ "type": "pipeline",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/dbt.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/dora.json b/config-ui/src/registry/plugins/dora.json
new file mode 100644
index 00000000..8fef1081
--- /dev/null
+++ b/config-ui/src/registry/plugins/dora.json
@@ -0,0 +1,12 @@
+{
+ "id": "dora",
+ "name": "DORA",
+ "type": "webhook",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/dora.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/feishu.json b/config-ui/src/registry/plugins/feishu.json
new file mode 100644
index 00000000..c7a704fe
--- /dev/null
+++ b/config-ui/src/registry/plugins/feishu.json
@@ -0,0 +1,12 @@
+{
+ "id": "feishu",
+ "name": "Feishu",
+ "type": "pipeline",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/feishu.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/gitee.json b/config-ui/src/registry/plugins/gitee.json
new file mode 100644
index 00000000..575d97e6
--- /dev/null
+++ b/config-ui/src/registry/plugins/gitee.json
@@ -0,0 +1,56 @@
+{
+ "id": "gitee",
+ "name": "Gitee",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/gitee.svg",
+ "private": false,
+ "connection": {
+ "authentication": "token",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "username": { },
+ "password": { },
+ "token": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "username": "Username",
+ "password": "Password",
+ "token": "Access Token",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. Gitee",
+ "endpoint": "URL eg. https://api.gitee.io/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "token": "eg. ff9d1ad0e5c04f1f98fa",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CICD"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ }
+ },
+ "default": {
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/gitextractor.json b/config-ui/src/registry/plugins/gitextractor.json
new file mode 100644
index 00000000..0060a841
--- /dev/null
+++ b/config-ui/src/registry/plugins/gitextractor.json
@@ -0,0 +1,12 @@
+{
+ "id": "gitextractor",
+ "name": "GitExtractor",
+ "type": "pipeline",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/gitextractor.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/github.json b/config-ui/src/registry/plugins/github.json
new file mode 100644
index 00000000..37e6e5b9
--- /dev/null
+++ b/config-ui/src/registry/plugins/github.json
@@ -0,0 +1,63 @@
+{
+ "id": "github",
+ "name": "GitHub",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/github.svg",
+ "private": false,
+ "connection": {
+ "authentication": "token",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "token": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "token": "Basic Auth Token",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. GitHub",
+ "endpoint": "eg. https://api.github.com/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "token": "eg. 4c5cbdb62c165e2b3d18, 40008ebccff9837bb8d2",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "token": "Due to Github's rate limit, input more tokens, \ncomma separated, to accelerate data collection.",
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CODE", "TICKET", "CROSS"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ "owner": null,
+ "repo": null
+ }
+ },
+ "default": {
+ "prType": "",
+ "prComponent": "",
+ "prBodyClosePattern": "",
+ "issueSeverity": "",
+ "issueComponent": "",
+ "issuePriority": "",
+ "issueTypeRequirement": "",
+ "issueTypeBug": "",
+ "issueTypeIncident": "",
+ "refdiff": null,
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/gitlab.json b/config-ui/src/registry/plugins/gitlab.json
new file mode 100644
index 00000000..4e95e87e
--- /dev/null
+++ b/config-ui/src/registry/plugins/gitlab.json
@@ -0,0 +1,52 @@
+{
+ "id": "gitlab",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "name": "GitLab",
+ "icon": "src/images/integrations/gitlab.svg",
+ "private": false,
+ "connection": {
+ "authentication": "token",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "token": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "token": "Access Token",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. GitLab",
+ "endpoint": "eg. https://gitlab.com/api/v4/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "token": "eg. ff9d1ad0e5c04f1f98fa",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CODE", "TICKET", "CROSS", "CICD"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ "projectId": null,
+ "title": null
+ }
+ },
+ "default": {
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/jenkins.json b/config-ui/src/registry/plugins/jenkins.json
new file mode 100644
index 00000000..806e82e9
--- /dev/null
+++ b/config-ui/src/registry/plugins/jenkins.json
@@ -0,0 +1,54 @@
+{
+ "id": "jenkins",
+ "name": "Jenkins",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/jenkins.svg",
+ "private": false,
+ "connection": {
+ "authentication": "plain",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "username": { },
+ "password": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "username": "Username",
+ "password": "Password",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. Jenkins",
+ "endpoint": "URL eg. https://api.jenkins.io/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "rateLimitPerHour": "1000"
+ },
+ "tooltips": {
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+ }
+ },
+ "entities": ["CICD"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ "jobName": null
+ }
+ },
+ "default": {
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/jira.json b/config-ui/src/registry/plugins/jira.json
new file mode 100644
index 00000000..3cce3141
--- /dev/null
+++ b/config-ui/src/registry/plugins/jira.json
@@ -0,0 +1,65 @@
+{
+ "id": "jira",
+ "name": "JIRA",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": false,
+ "isProvider": true,
+ "icon": "src/images/integrations/jira.svg",
+ "private": false,
+ "connection": {
+ "authentication": "plain",
+ "fields": {
+ "name": {
+ },
+ "endpoint": { },
+ "username": { },
+ "password": { },
+ "proxy": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "username": "Username / E-mail",
+ "password": "Password",
+ "proxy": "Proxy URL",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. JIRA",
+ "endpoint": "eg. https://your-domain.atlassian.net/rest/",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "rateLimit": "1000"
+ },
+ "tooltips": {
+ "password": "If you are using JIRA Cloud or JIRA Server,\nyour API Token should be used as password.",
+ "rateLimitPerHour": "Rate Limit requests per hour,\nEnter a numeric value > 0 to enable."
+
+ }
+ },
+ "entities": ["TICKET", "CROSS"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ "boardId": null,
+ "title": null
+ }
+ },
+ "default": {
+ "epicKeyField": "",
+ "typeMappings": {},
+ "storyPointField": "",
+ "remotelinkCommitShaPattern": "",
+ "bugTags": [],
+ "incidentTags": [],
+ "requirementTags": [],
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/refdiff.json b/config-ui/src/registry/plugins/refdiff.json
new file mode 100644
index 00000000..02708925
--- /dev/null
+++ b/config-ui/src/registry/plugins/refdiff.json
@@ -0,0 +1,12 @@
+{
+ "id": "refdiff",
+ "name": "RefDiff",
+ "type": "pipeline",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/refdiff.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/starrocks.json b/config-ui/src/registry/plugins/starrocks.json
new file mode 100644
index 00000000..444b5a89
--- /dev/null
+++ b/config-ui/src/registry/plugins/starrocks.json
@@ -0,0 +1,12 @@
+{
+ "id": "starrocks",
+ "name": "Starrocks",
+ "type": "pipeline",
+ "enabled": true,
+ "multiConnection": false,
+ "isBeta": false,
+ "isProvider": false,
+ "icon": "src/images/integrations/starrocks.svg",
+ "private": false,
+ "connection": null
+}
\ No newline at end of file
diff --git a/config-ui/src/registry/plugins/tapd.json b/config-ui/src/registry/plugins/tapd.json
new file mode 100644
index 00000000..9a30999f
--- /dev/null
+++ b/config-ui/src/registry/plugins/tapd.json
@@ -0,0 +1,55 @@
+{
+ "id": "tapd",
+ "name": "TAPD",
+ "type": "integration",
+ "enabled": true,
+ "multiConnection": true,
+ "connectionLimit": 0,
+ "isBeta": true,
+ "isProvider": true,
+ "icon": "src/images/integrations/tapd.svg",
+ "private": false,
+ "connection": {
+ "authentication": "plain",
+ "fields": {
+ "name": { "enable": true, "required": true, "readonly": false },
+ "endpoint": { },
+ "proxy": { },
+ "username": { },
+ "password": { },
+ "rateLimitPerHour": { }
+ },
+ "labels": {
+ "name": "Connection Name",
+ "endpoint": "Endpoint URL",
+ "proxy": "Proxy URL",
+ "username": "Username",
+ "password": "Password",
+ "rateLimitPerHour": "Rate Limit (per hour)"
+ },
+ "placeholders": {
+ "name": "eg. Tapd",
+ "endpoint": "URL eg. https://api.tapd.cn/",
+ "proxy": "eg. http://proxy.localhost:8080",
+ "username": "eg. admin",
+ "password": "eg. ************",
+ "rateLimit": "1000"
+ },
+ "tooltips": {
+ }
+ },
+ "entities": ["CODE", "TICKET"],
+ "transformations": {
+ "scopes": {
+ "options": {
+ }
+ },
+ "default": {
+ "issueTypeRequirement": "",
+ "issueTypeBug": "",
+ "issueTypeIncident": "",
+ "productionPattern": "",
+ "deploymentPattern": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/config-ui/src/store/integrations-context.jsx b/config-ui/src/store/integrations-context.jsx
new file mode 100644
index 00000000..37b0a133
--- /dev/null
+++ b/config-ui/src/store/integrations-context.jsx
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import React, { useState } from 'react'
+import useIntegrations from '@/hooks/useIntegrations'
+
+const IntegrationsContext = React.createContext({
+ registry: {},
+ plugins: {},
+ integrations: {},
+ activeProvider: null,
+ Providers: {},
+ ProviderIcons: {},
+ ProviderFormLabels: {},
+ ProviderFormPlaceholders: {},
+ ProviderConnectionLimits: {},
+ ProviderTransformations: {},
+ setActiveProvider: () => {}
+})
+
+export const IntegrationsContextProvider = (props) => {
+ const {
+ registry: Registry,
+ plugins: Plugins,
+ integrations: Integrations,
+ activeProvider,
+ Providers,
+ ProviderLabels,
+ ProviderIcons,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderConnectionLimits,
+ ProviderTransformations,
+ setActiveProvider
+ } = useIntegrations()
+
+ const contextValue = {
+ Registry,
+ Plugins,
+ Integrations,
+ activeProvider,
+ Providers,
+ ProviderLabels,
+ ProviderIcons,
+ ProviderFormLabels,
+ ProviderFormPlaceholders,
+ ProviderConnectionLimits,
+ ProviderTransformations,
+ setActiveProvider
+ }
+
+ return (
+ <IntegrationsContext.Provider value={contextValue}>
+ {props.children}
+ </IntegrationsContext.Provider>
+ )
+}
+
+export default IntegrationsContext