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 0aae83c21 refactor(config-ui): the connections page (#4046)
0aae83c21 is described below
commit 0aae83c21fe11d90499b260dbd72a2423d97d025
Author: 青湛 <[email protected]>
AuthorDate: Wed Dec 28 21:39:14 2022 +0800
refactor(config-ui): the connections page (#4046)
* feat(config-ui): add connection status component
* fix(config-ui): adjust the type for plugin config
* refactor(config-ui): the connection list page
* refactor(config-ui): the connection detail page
* refactor(config-ui): use new connection page to replace old
---
config-ui/src/App.js | 23 +-
.../config.ts => pages/connection/form/api.ts} | 25 +-
config-ui/src/pages/connection/form/index.tsx | 270 +++++++++++++++++++++
config-ui/src/pages/connection/form/styled.ts | 88 +++++++
config-ui/src/pages/connection/form/use-form.ts | 105 ++++++++
config-ui/src/pages/connection/index.ts | 3 +-
.../connection/{webhook/index.ts => list/api.ts} | 6 +-
config-ui/src/pages/connection/list/connection.tsx | 148 +++++++++++
config-ui/src/pages/connection/list/index.tsx | 53 ++++
.../config.ts => pages/connection/list/styled.ts} | 37 ++-
config-ui/src/plugins/types.ts | 21 +-
config-ui/src/plugins/webook/config.ts | 7 +-
config-ui/src/store/connections/index.ts | 1 +
config-ui/src/store/connections/status.tsx | 83 +++++++
14 files changed, 823 insertions(+), 47 deletions(-)
diff --git a/config-ui/src/App.js b/config-ui/src/App.js
index 06999a60d..9fed8d79f 100644
--- a/config-ui/src/App.js
+++ b/config-ui/src/App.js
@@ -29,16 +29,14 @@ import {
ProjectHomePage,
ProjectDetailPage,
ConnectionHomePage,
- WebHookConnectionPage,
+ ConnectionListPage,
+ ConnectionFormPage,
CreateBlueprintPage,
BlueprintHomePage,
BlueprintDetailPage,
TransformationHomePage,
TransformationDetailPage
} from '@/pages'
-import ManageIntegration from '@/pages/configure/integration/manage'
-import AddConnection from '@/pages/configure/connections/AddConnection'
-import ConfigureConnection from
'@/pages/configure/connections/ConfigureConnection'
function App() {
return (
@@ -63,23 +61,18 @@ function App() {
/>
<Route
exact
- path='/connections/webhook'
- component={() => <WebHookConnectionPage />}
+ path='/connections/:plugin'
+ component={() => <ConnectionListPage />}
/>
<Route
exact
- path='/connections/:providerId'
- component={() => <ManageIntegration />}
+ path='/connections/:plugin/create'
+ component={() => <ConnectionFormPage />}
/>
<Route
exact
- path='/connections/add/:providerId'
- component={() => <AddConnection />}
- />
- <Route
- exact
- path='/connections/configure/:providerId/:connectionId'
- component={() => <ConfigureConnection />}
+ path='/connections/:plugin/:cid'
+ component={() => <ConnectionFormPage />}
/>
<Route
exact
diff --git a/config-ui/src/plugins/webook/config.ts
b/config-ui/src/pages/connection/form/api.ts
similarity index 55%
copy from config-ui/src/plugins/webook/config.ts
copy to config-ui/src/pages/connection/form/api.ts
index d7fa2cbae..2a249ed50 100644
--- a/config-ui/src/plugins/webook/config.ts
+++ b/config-ui/src/pages/connection/form/api.ts
@@ -16,15 +16,20 @@
*
*/
-import { Plugins, PluginType } from '@/plugins'
+import { Plugins } from '@/plugins'
+import request from '@/components/utils/request'
-import Icon from './assets/icon.svg'
+export const testConnection = (plugin: Plugins, payload: any) =>
+ request(`/plugins/${plugin}/test`, { method: 'post', data: payload })
-export const WebhookConfig = {
- plugin: Plugins.Webhook,
- name: 'Webhook',
- type: PluginType.Incoming_Connection,
- icon: Icon,
- entities: [],
- transformation: {}
-}
+export const createConnection = (plugin: Plugins, payload: any) =>
+ request(`/plugins/${plugin}/connections`, { method: 'post', data: payload })
+
+export const getConnection = (plugin: Plugins, id: ID) =>
+ request(`/plugins/${plugin}/connections/${id}`)
+
+export const updateConnection = (plugin: Plugins, id: ID, payload: any) =>
+ request(`/plugins/${plugin}/connections/${id}`, {
+ method: 'patch',
+ data: payload
+ })
diff --git a/config-ui/src/pages/connection/form/index.tsx
b/config-ui/src/pages/connection/form/index.tsx
new file mode 100644
index 000000000..ee5d024a3
--- /dev/null
+++ b/config-ui/src/pages/connection/form/index.tsx
@@ -0,0 +1,270 @@
+/*
+ * 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, useEffect, useMemo } from 'react'
+import { useParams, useHistory } from 'react-router-dom'
+import { omit, pick } from 'lodash'
+import {
+ FormGroup,
+ InputGroup,
+ NumericInput,
+ Switch,
+ ButtonGroup,
+ Button,
+ Icon,
+ Intent,
+ Position
+} from '@blueprintjs/core'
+import { Tooltip2 } from '@blueprintjs/popover2'
+
+import { PageHeader, Card, PageLoading } from '@/components'
+import type { PluginConfigConnectionType } from '@/plugins'
+import { Plugins, PluginConfig } from '@/plugins'
+
+import { useForm } from './use-form'
+import * as S from './styled'
+
+export const ConnectionFormPage = () => {
+ const [form, setForm] = useState<Record<string, any>>({})
+ const [showRateLimit, setShowRateLimit] = useState(false)
+ const [githubTokens, setGitHubTokens] = useState<Record<string, string>>({
+ '0': '',
+ '1': ''
+ })
+
+ const history = useHistory()
+ const { plugin, cid } = useParams<{ plugin: Plugins; cid?: string }>()
+ const { loading, operating, connection, onTest, onCreate, onUpdate } =
+ useForm({ plugin, id: cid })
+
+ const {
+ name,
+ connection: { initialValues, fields }
+ } = useMemo(
+ () =>
+ PluginConfig.find(
+ (p) => p.plugin === plugin
+ ) as PluginConfigConnectionType,
+ [plugin]
+ )
+
+ useEffect(() => {
+ setForm({
+ ...form,
+ ...(initialValues ?? {}),
+ ...(connection ?? {}),
+ token: Object.values(githubTokens).filter(Boolean).join(',')
+ })
+ }, [githubTokens, initialValues, connection])
+
+ const error = useMemo(
+ () =>
+ !!(fields.filter((field) => field.required) ?? []).find(
+ (field) => !form[field.key]
+ ),
+ [form, fields]
+ )
+
+ const handleChangeGitHubToken = (key: string, token: string) => {
+ setGitHubTokens({
+ ...githubTokens,
+ [key]: token
+ })
+ }
+
+ const handleCreateToken = () => {
+ const keys = Object.keys(githubTokens)
+ setGitHubTokens({
+ ...githubTokens,
+ [+keys[keys.length - 1] + 1]: ''
+ })
+ }
+
+ const handleRemoveToken = (key: string) => {
+ setGitHubTokens(omit(githubTokens, [key]))
+ }
+
+ const handleTest = () =>
+ onTest(
+ pick(form, [
+ 'endpoint',
+ 'token',
+ 'username',
+ 'password',
+ 'app_id',
+ 'secret_key',
+ 'proxy'
+ ])
+ )
+
+ const handleCancel = () => history.push(`/connections/${plugin}`)
+
+ const handleSave = () => (cid ? onUpdate(cid, form) : onCreate(form))
+
+ const getFormItem = ({
+ key,
+ label,
+ type,
+ required,
+ placeholder,
+ tooltip
+ }: PluginConfigConnectionType['connection']['fields']['0']) => {
+ return (
+ <FormGroup
+ key={key}
+ inline
+ label={
+ <S.Label>
+ <span>{label}</span>
+ {tooltip && (
+ <Tooltip2 position={Position.TOP} content={tooltip}>
+ <Icon icon='help' size={12} />
+ </Tooltip2>
+ )}
+ </S.Label>
+ }
+ labelFor={key}
+ labelInfo={required ? '*' : ''}
+ >
+ {type === 'github-token' && (
+ <S.GitHubToken>
+ <p>
+ Add one or more personal token(s) for authentication from you and
+ your organization members. Multiple tokens can help speed up the
+ data collection process.{' '}
+ </p>
+ <p>
+ <a
+
href='https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token'
+ target='_blank'
+ rel='noreferrer'
+ >
+ Learn about how to create a personal access token
+ </a>
+ </p>
+ <h3>Personal Access Token(s)</h3>
+ {Object.entries(githubTokens).map(([key, value]) => (
+ <div className='token' key={key}>
+ <InputGroup
+ placeholder='token'
+ value={value}
+ onChange={(e) => handleChangeGitHubToken(key,
e.target.value)}
+ />
+ <Button
+ minimal
+ icon='cross'
+ onClick={() => handleRemoveToken(key)}
+ />
+ </div>
+ ))}
+ <div className='action'>
+ <Button
+ outlined
+ small
+ intent={Intent.PRIMARY}
+ text='Another Token'
+ icon='plus'
+ onClick={handleCreateToken}
+ />
+ </div>
+ </S.GitHubToken>
+ )}
+ {type === 'text' && (
+ <InputGroup
+ placeholder={placeholder}
+ value={form[key] ?? ''}
+ onChange={(e) => setForm({ ...form, [`${key}`]: e.target.value })}
+ />
+ )}
+ {type === 'numeric' && (
+ <S.RateLimit>
+ {showRateLimit && (
+ <NumericInput
+ placeholder={placeholder}
+ onValueChange={(value) =>
+ setForm({
+ ...form,
+ [key]: value
+ })
+ }
+ />
+ )}
+ <Switch
+ checked={showRateLimit}
+ onChange={(e) =>
+ setShowRateLimit((e.target as HTMLInputElement).checked)
+ }
+ />
+ </S.RateLimit>
+ )}
+ {type === 'switch' && (
+ <Switch
+ checked={form[key] ?? false}
+ onChange={(e) =>
+ setForm({
+ ...form,
+ [key]: (e.target as HTMLInputElement).checked
+ })
+ }
+ />
+ )}
+ </FormGroup>
+ )
+ }
+
+ return (
+ <PageHeader
+ breadcrumbs={[
+ { name: 'Connections', path: '/connections' },
+ { name, path: `/connections/${plugin}` },
+ {
+ name: cid ? cid : 'Create',
+ path: `/connections/${plugin}/${cid ? cid : 'create'}`
+ }
+ ]}
+ >
+ {loading ? (
+ <PageLoading />
+ ) : (
+ <Card>
+ <S.Wrapper>
+ {fields.map((field) => getFormItem(field))}
+ <div className='footer'>
+ <Button
+ disabled={error}
+ loading={operating}
+ text='Test Connection'
+ onClick={handleTest}
+ />
+ <ButtonGroup>
+ <Button text='Cancel' onClick={handleCancel} />
+ <Button
+ disabled={error}
+ loading={operating}
+ intent={Intent.PRIMARY}
+ text='Save Connection'
+ onClick={handleSave}
+ />
+ </ButtonGroup>
+ </div>
+ </S.Wrapper>
+ </Card>
+ )}
+ </PageHeader>
+ )
+}
diff --git a/config-ui/src/pages/connection/form/styled.ts
b/config-ui/src/pages/connection/form/styled.ts
new file mode 100644
index 000000000..16290d2bf
--- /dev/null
+++ b/config-ui/src/pages/connection/form/styled.ts
@@ -0,0 +1,88 @@
+/*
+ * 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 { Colors } from '@blueprintjs/core'
+import styled from 'styled-components'
+
+export const Wrapper = styled.div`
+ .bp4-form-group {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .bp4-label {
+ flex: 0 0 200px;
+ font-weight: 600;
+
+ .bp4-popover2-target {
+ display: inline;
+ margin: 0;
+ line-height: 1;
+ margin-left: 4px;
+ }
+
+ .bp4-text-muted {
+ color: ${Colors.RED3};
+ }
+ }
+
+ .bp4-form-content {
+ flex: auto;
+ }
+ }
+
+ .footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 32px;
+ }
+`
+
+export const Label = styled.span`
+ display: inline-flex;
+ align-items: center;
+`
+
+export const RateLimit = styled.div`
+ display: flex;
+ align-items: center;
+
+ & > .bp4-numeric-input {
+ margin-right: 8px;
+ }
+`
+
+export const GitHubToken = styled.div`
+ p {
+ margin: 0 0 8px;
+ }
+
+ h3 {
+ margin: 0 0 8px;
+ padding: 0;
+ font-size: 14px;
+ }
+
+ .token {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ }
+`
diff --git a/config-ui/src/pages/connection/form/use-form.ts
b/config-ui/src/pages/connection/form/use-form.ts
new file mode 100644
index 000000000..555403d4d
--- /dev/null
+++ b/config-ui/src/pages/connection/form/use-form.ts
@@ -0,0 +1,105 @@
+/*
+ * 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 { useState, useMemo, useEffect } from 'react'
+import { useHistory } from 'react-router-dom'
+
+import { Plugins } from '@/plugins'
+import { operator } from '@/utils'
+
+import * as API from './api'
+
+interface Props {
+ plugin: Plugins
+ id?: ID
+}
+
+export const useForm = ({ plugin, id }: Props) => {
+ const [loading, setLoading] = useState(false)
+ const [operating, setOperating] = useState(false)
+ const [connection, setConnection] = useState()
+
+ const history = useHistory()
+
+ const getConnection = async () => {
+ if (!id) return
+
+ setLoading(true)
+ try {
+ const res = await API.getConnection(plugin, id)
+ setConnection(res)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ useEffect(() => {
+ getConnection()
+ }, [])
+
+ const handleTest = async (payload: any) => {
+ const [success] = await operator(
+ () => API.testConnection(plugin, payload),
+ {
+ setOperating,
+ formatReason: () => 'Test connection link failed.'
+ }
+ )
+
+ if (success) {
+ }
+ }
+
+ const handleCreate = async (payload: any) => {
+ const [success] = await operator(
+ () => API.createConnection(plugin, payload),
+ {
+ setOperating
+ }
+ )
+
+ if (success) {
+ history.push(`/connections/${plugin}`)
+ }
+ }
+
+ const handleUpdate = async (id: ID, payload: any) => {
+ const [success] = await operator(
+ () => API.updateConnection(plugin, id, payload),
+ {
+ setOperating
+ }
+ )
+
+ if (success) {
+ history.push(`/connections/${plugin}`)
+ }
+ }
+
+ return useMemo(
+ () => ({
+ loading,
+ operating,
+ connection,
+ onTest: handleTest,
+ onCreate: handleCreate,
+ onUpdate: handleUpdate
+ }),
+ [loading, operating, connection]
+ )
+}
diff --git a/config-ui/src/pages/connection/index.ts
b/config-ui/src/pages/connection/index.ts
index af1444500..e8f447932 100644
--- a/config-ui/src/pages/connection/index.ts
+++ b/config-ui/src/pages/connection/index.ts
@@ -17,4 +17,5 @@
*/
export * from './home'
-export * from './webhook'
+export * from './list'
+export * from './form'
diff --git a/config-ui/src/pages/connection/webhook/index.ts
b/config-ui/src/pages/connection/list/api.ts
similarity index 78%
rename from config-ui/src/pages/connection/webhook/index.ts
rename to config-ui/src/pages/connection/list/api.ts
index 66ed7d6f5..8d8e0e666 100644
--- a/config-ui/src/pages/connection/webhook/index.ts
+++ b/config-ui/src/pages/connection/list/api.ts
@@ -16,6 +16,8 @@
*
*/
-import { WebHookConnection } from '@/plugins'
+import { Plugins } from '@/plugins'
+import request from '@/components/utils/request'
-export const WebHookConnectionPage = WebHookConnection
+export const deleteConnection = (plugin: Plugins, id: ID) =>
+ request(`/plugins/${plugin}/connections/${id}`, { method: 'delete' })
diff --git a/config-ui/src/pages/connection/list/connection.tsx
b/config-ui/src/pages/connection/list/connection.tsx
new file mode 100644
index 000000000..33cc7460e
--- /dev/null
+++ b/config-ui/src/pages/connection/list/connection.tsx
@@ -0,0 +1,148 @@
+/*
+ * 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, useEffect, useMemo } from 'react'
+import { useHistory } from 'react-router-dom'
+import { ButtonGroup, Button, Intent, Position } from '@blueprintjs/core'
+import { Popover2 } from '@blueprintjs/popover2'
+
+import { Table, ColumnType } from '@/components'
+import { Plugins } from '@/plugins'
+import type { ConnectionItemType } from '@/store'
+import { useConnection, ConnectionStatus } from '@/store'
+import { operator } from '@/utils'
+
+import * as API from './api'
+import * as S from './styled'
+
+interface Props {
+ plugin: Plugins
+}
+
+export const Connection = ({ plugin }: Props) => {
+ const [operating, setOperating] = useState(false)
+
+ const history = useHistory()
+
+ const { connections, onTest, onRefresh } = useConnection()
+
+ useEffect(() => {
+ connections.map((cs) => onTest(cs))
+ }, [])
+
+ const handleRefresh = () => onRefresh()
+
+ const handleCreate = () => history.push(`/connections/${plugin}/create`)
+
+ const handleUpdate = (id: ID) =>
+ history.push(`/connections/${plugin}/${id}`)
+
+ const handleDelete = async (id: ID) => {
+ const [success] = await operator(() => API.deleteConnection(plugin, id), {
+ setOperating
+ })
+
+ if (success) {
+ onRefresh()
+ }
+ }
+
+ const columns = useMemo(
+ () =>
+ [
+ {
+ title: 'ID',
+ dataIndex: 'id',
+ key: 'id'
+ },
+ {
+ title: 'Connection Name',
+ dataIndex: 'name',
+ key: 'name'
+ },
+ {
+ title: 'Endpoint',
+ dataIndex: 'endpoint',
+ key: 'endpoint'
+ },
+ {
+ title: 'Status',
+ dataIndex: 'status',
+ key: 'status',
+ align: 'center',
+ render: (_, row) => (
+ <ConnectionStatus connection={row} onTest={onTest} />
+ )
+ },
+ {
+ title: '',
+ dataIndex: 'id',
+ key: 'action',
+ render: (id) => (
+ <ButtonGroup>
+ <Button
+ minimal
+ intent={Intent.PRIMARY}
+ icon='edit'
+ onClick={() => handleUpdate(id)}
+ />
+ <Popover2
+ position={Position.TOP}
+ content={
+ <S.DeleteConfirm>
+ <h3>Confirm deletion</h3>
+ <p>Are you sure you want to delete this item?</p>
+ <ButtonGroup>
+ <Button
+ loading={operating}
+ intent={Intent.DANGER}
+ text='Delete'
+ onClick={() => handleDelete(id)}
+ />
+ </ButtonGroup>
+ </S.DeleteConfirm>
+ }
+ >
+ <Button minimal intent={Intent.PRIMARY} icon='delete' />
+ </Popover2>
+ </ButtonGroup>
+ )
+ }
+ ] as ColumnType<ConnectionItemType>,
+ []
+ )
+
+ return (
+ <S.Wrapper>
+ <ButtonGroup className='action'>
+ <Button
+ intent={Intent.PRIMARY}
+ icon='plus'
+ text='Add Connection'
+ onClick={handleCreate}
+ />
+ <Button
+ icon='refresh'
+ text='Refresh Connections'
+ onClick={handleRefresh}
+ />
+ </ButtonGroup>
+ <Table columns={columns} dataSource={connections} />
+ </S.Wrapper>
+ )
+}
diff --git a/config-ui/src/pages/connection/list/index.tsx
b/config-ui/src/pages/connection/list/index.tsx
new file mode 100644
index 000000000..98a46de2f
--- /dev/null
+++ b/config-ui/src/pages/connection/list/index.tsx
@@ -0,0 +1,53 @@
+/*
+ * 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, { useMemo } from 'react'
+import { useParams } from 'react-router-dom'
+
+import { PageHeader } from '@/components'
+import type { PluginConfigType } from '@/plugins'
+import { Plugins, PluginConfig, WebHookConnection } from '@/plugins'
+import { ConnectionContextProvider } from '@/store'
+
+import { Connection } from './connection'
+
+export const ConnectionListPage = () => {
+ const { plugin } = useParams<{ plugin: Plugins }>()
+
+ const config = useMemo(
+ () => PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType,
+ [plugin]
+ )
+
+ return (
+ <ConnectionContextProvider plugins={[plugin]}>
+ <PageHeader
+ breadcrumbs={[
+ { name: 'Connections', path: '/connections' },
+ { name: config.name, path: `/connections/${plugin}` }
+ ]}
+ >
+ {plugin === Plugins.Webhook ? (
+ <WebHookConnection />
+ ) : (
+ <Connection plugin={plugin} />
+ )}
+ </PageHeader>
+ </ConnectionContextProvider>
+ )
+}
diff --git a/config-ui/src/plugins/webook/config.ts
b/config-ui/src/pages/connection/list/styled.ts
similarity index 67%
copy from config-ui/src/plugins/webook/config.ts
copy to config-ui/src/pages/connection/list/styled.ts
index d7fa2cbae..f8b1f5507 100644
--- a/config-ui/src/plugins/webook/config.ts
+++ b/config-ui/src/pages/connection/list/styled.ts
@@ -16,15 +16,32 @@
*
*/
-import { Plugins, PluginType } from '@/plugins'
+import styled from 'styled-components'
-import Icon from './assets/icon.svg'
+export const Wrapper = styled.div`
+ .action {
+ margin-bottom: 16px;
-export const WebhookConfig = {
- plugin: Plugins.Webhook,
- name: 'Webhook',
- type: PluginType.Incoming_Connection,
- icon: Icon,
- entities: [],
- transformation: {}
-}
+ .bp4-button + .bp4-button {
+ margin-left: 8px;
+ }
+ }
+`
+
+export const DeleteConfirm = styled.div`
+ padding: 16px 24px;
+
+ h3 {
+ margin: 0;
+ padding: 0;
+ }
+
+ p {
+ margin: 8px 0;
+ }
+
+ .bp4-button-group {
+ display: flex;
+ justify-content: flex-end;
+ }
+`
diff --git a/config-ui/src/plugins/types.ts b/config-ui/src/plugins/types.ts
index 7ce5e6570..dd4e969bc 100644
--- a/config-ui/src/plugins/types.ts
+++ b/config-ui/src/plugins/types.ts
@@ -43,12 +43,12 @@ export enum PluginType {
Pipeline = 'pipeline'
}
-export type PluginConfigType = {
+export type PluginConfigConnectionType = {
plugin: Plugins
name: string
- type: PluginType
+ type: PluginType.Connection
icon: string
- connection?: {
+ connection: {
initialValues?: Record<string, any>
fields: Array<{
key: string
@@ -59,6 +59,17 @@ export type PluginConfigType = {
tooltip?: string
}>
}
- entities?: string[]
- transformation?: any
+ entities: string[]
+ transformation: any
}
+
+export type PluginConfigAnotherType = {
+ plugin: Plugins
+ name: string
+ type: PluginType.Incoming_Connection | PluginType.Pipeline
+ icon: string
+}
+
+export type PluginConfigType =
+ | PluginConfigConnectionType
+ | PluginConfigAnotherType
diff --git a/config-ui/src/plugins/webook/config.ts
b/config-ui/src/plugins/webook/config.ts
index d7fa2cbae..42b1e4f13 100644
--- a/config-ui/src/plugins/webook/config.ts
+++ b/config-ui/src/plugins/webook/config.ts
@@ -16,15 +16,14 @@
*
*/
+import type { PluginConfigType } from '@/plugins'
import { Plugins, PluginType } from '@/plugins'
import Icon from './assets/icon.svg'
-export const WebhookConfig = {
+export const WebhookConfig: PluginConfigType = {
plugin: Plugins.Webhook,
name: 'Webhook',
type: PluginType.Incoming_Connection,
- icon: Icon,
- entities: [],
- transformation: {}
+ icon: Icon
}
diff --git a/config-ui/src/store/connections/index.ts
b/config-ui/src/store/connections/index.ts
index 5f25db57c..d2bcc20ec 100644
--- a/config-ui/src/store/connections/index.ts
+++ b/config-ui/src/store/connections/index.ts
@@ -18,3 +18,4 @@
export * from './types'
export * from './context'
+export * from './status'
diff --git a/config-ui/src/store/connections/status.tsx
b/config-ui/src/store/connections/status.tsx
new file mode 100644
index 000000000..fa0ed6de4
--- /dev/null
+++ b/config-ui/src/store/connections/status.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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 from 'react'
+import { Icon, Colors, Position, Intent } from '@blueprintjs/core'
+import { Tooltip2 } from '@blueprintjs/popover2'
+import styled from 'styled-components'
+
+import { Loading } from '@/components'
+
+import type { ConnectionItemType } from './types'
+import { ConnectionStatusEnum } from './types'
+
+const Wrapper = styled.div`
+ display: inline-flex;
+ align-items: center;
+
+ & > span.online {
+ color: ${Colors.GREEN3};
+ }
+
+ & > span.offline {
+ color: ${Colors.RED3};
+ }
+
+ & > span.testing {
+ color: #7497f7;
+ }
+`
+
+const STATUS_MAP = {
+ [`${ConnectionStatusEnum.NULL}`]: 'Init',
+ [`${ConnectionStatusEnum.TESTING}`]: 'Testing',
+ [`${ConnectionStatusEnum.ONLINE}`]: 'Online',
+ [`${ConnectionStatusEnum.OFFLINE}`]: 'Offline'
+}
+
+interface Props {
+ connection: ConnectionItemType
+ onTest: (connection: ConnectionItemType) => void
+}
+
+export const ConnectionStatus = ({ connection, onTest }: Props) => {
+ const { status } = connection
+
+ return (
+ <Wrapper>
+ {status === ConnectionStatusEnum.TESTING && (
+ <Loading size={14} style={{ marginRight: 4 }} />
+ )}
+ {status === ConnectionStatusEnum.OFFLINE && (
+ <Tooltip2
+ intent={Intent.PRIMARY}
+ position={Position.TOP}
+ content='Retry'
+ >
+ <Icon
+ size={14}
+ icon='repeat'
+ style={{ marginRight: 4, color: Colors.RED3, cursor: 'pointer' }}
+ onClick={() => onTest(connection)}
+ />
+ </Tooltip2>
+ )}
+ <span className={status}>{STATUS_MAP[status]}</span>
+ </Wrapper>
+ )
+}