feat(frontend): add tapd config-ui
Signed-off-by: Yingchu Chen <yingchu.chen@merico.dev>
diff --git a/config-ui/src/components/Sidebar/MenuConfiguration.jsx b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
index c6fad4d..a291c47 100644
--- a/config-ui/src/components/Sidebar/MenuConfiguration.jsx
+++ b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
@@ -59,6 +59,14 @@
active: activeRoute.url.endsWith('/integrations/jenkins') || activeRoute.url.endsWith('/jenkins'),
icon: 'layers',
classNames: [],
+ },
+ {
+ id: 4,
+ label: ProviderLabels.TAPD,
+ route: '/integrations/tapd',
+ active: activeRoute.url.endsWith('/integrations/tapd') || activeRoute.url.endsWith('/tapd'),
+ icon: 'layers',
+ classNames: [],
}
]
},
diff --git a/config-ui/src/components/blueprints/ConnectionDialog.jsx b/config-ui/src/components/blueprints/ConnectionDialog.jsx
index 05079dc..0424562 100644
--- a/config-ui/src/components/blueprints/ConnectionDialog.jsx
+++ b/config-ui/src/components/blueprints/ConnectionDialog.jsx
@@ -75,6 +75,12 @@
title: ProviderLabels[Providers.JENKINS.toUpperCase()],
value: Providers.JENKINS,
},
+ {
+ id: 5,
+ name: Providers.TAPD,
+ title: ProviderLabels[Providers.TAPD.toUpperCase()],
+ value: Providers.TAPD,
+ },
]
const ConnectionDialog = (props) => {
diff --git a/config-ui/src/components/pipelines/StageTaskName.jsx b/config-ui/src/components/pipelines/StageTaskName.jsx
index 8061039..cb84f88 100644
--- a/config-ui/src/components/pipelines/StageTaskName.jsx
+++ b/config-ui/src/components/pipelines/StageTaskName.jsx
@@ -92,6 +92,7 @@
{task.plugin === Providers.GITEXTRACTOR && (<>{ProviderLabels.GITEXTRACTOR}</>)}
{task.plugin === Providers.FEISHU && (<>{ProviderLabels.FEISHU}</>)}
{task.plugin === Providers.JENKINS && (<>{ProviderLabels.JENKINS}</>)}
+ {task.plugin === Providers.TAPD && (<>{ProviderLabels.TAPD}</>)}
{task.plugin === Providers.JIRA && (<>Board ID {task.options.boardId}</>)}
{task.plugin === Providers.GITLAB && (<>Project ID {task.options.projectId}</>)}
{task.plugin === Providers.GITHUB && task.plugin !== Providers.JENKINS && (<>@{task.options.owner}/{task.options.repo}</>)}
diff --git a/config-ui/src/data/Providers.js b/config-ui/src/data/Providers.js
index e7db313..b5c0f41 100644
--- a/config-ui/src/data/Providers.js
+++ b/config-ui/src/data/Providers.js
@@ -21,6 +21,7 @@
import { ReactComponent as JenkinsProviderIcon } from '@/images/integrations/jenkins.svg'
import { ReactComponent as JiraProviderIcon } from '@/images/integrations/jira.svg'
import { ReactComponent as GitHubProviderIcon } from '@/images/integrations/github.svg'
+import { ReactComponent as TapdProviderIcon } from '@/images/integrations/tapd.svg'
// import GitExtractorIcon from '@/images/git.png'
// import RefDiffIcon from '@/images/git-diff.png'
import FeishuIcon from '@/images/feishu.png'
@@ -39,6 +40,7 @@
AE: 'ae',
DBT: 'dbt',
STARROCKS: 'starrocks',
+ TAPD: 'tapd',
}
const ProviderTypes = {
@@ -59,6 +61,7 @@
AE: 'Analysis Engine (AE)',
DBT: 'Data Build Tool (DBT)',
STARROCKS: 'StarRocks',
+ TAPD: 'Tapd',
}
const ProviderConnectionLimits = {
@@ -96,11 +99,18 @@
username: 'Username',
password: 'Password'
},
- jira: {
+ tapd: {
name: 'Connection Name',
endpoint: 'Endpoint URL',
proxy: 'Proxy URL',
token: 'Basic Auth Token',
+ username: 'Username',
+ password: 'Password',
+ },
+ jira: {
+ name: 'Connection Name',
+ endpoint: 'Endpoint URL',
+ token: 'Basic Auth Token',
username: 'Username / E-mail',
// password; 'Password',
password: (
@@ -181,6 +191,14 @@
username: 'eg. admin',
password: 'eg. ************'
},
+ tapd: {
+ name: 'eg. Tapd',
+ endpoint: 'URL eg. https://api.tapd.cn/',
+ proxy: 'eg. http://proxy.localhost:8080',
+ token: 'eg. 6b057ffe68464c93a057',
+ username: 'eg. admin',
+ password: 'eg. ************'
+ },
jira: {
name: 'eg. JIRA',
endpoint: 'eg. https://your-domain.atlassian.net/rest/',
@@ -202,6 +220,7 @@
const ProviderIcons = {
[Providers.GITLAB]: (w, h) => <GitlabProviderIcon width={w || 24} height={h || 24} />,
[Providers.JENKINS]: (w, h) => <JenkinsProviderIcon width={w || 24} height={h || 24} />,
+ [Providers.TAPD]: (w, h) => <TapdProviderIcon width={w || 24} height={h || 24} />,
[Providers.JIRA]: (w, h) => <JiraProviderIcon width={w || 24} height={h || 24} />,
[Providers.GITHUB]: (w, h) => <GitHubProviderIcon width={w || 24} height={h || 24} />,
[Providers.REFDIFF]: (w, h) => <Icon icon='box' size={w || 24} />,
diff --git a/config-ui/src/data/availablePlugins.js b/config-ui/src/data/availablePlugins.js
index 6827c54..4433f79 100644
--- a/config-ui/src/data/availablePlugins.js
+++ b/config-ui/src/data/availablePlugins.js
@@ -15,6 +15,6 @@
* limitations under the License.
*
*/
-const AVAILABLE_PLUGINS = ['gitlab', 'jira', 'jenkins', 'github']
+const AVAILABLE_PLUGINS = ['gitlab', 'jira', 'jenkins', 'github', 'tapd']
module.exports = AVAILABLE_PLUGINS
diff --git a/config-ui/src/data/integrations.jsx b/config-ui/src/data/integrations.jsx
index a63a0ae..10134c7 100644
--- a/config-ui/src/data/integrations.jsx
+++ b/config-ui/src/data/integrations.jsx
@@ -28,6 +28,7 @@
import { ReactComponent as JenkinsProvider } from '@/images/integrations/jenkins.svg'
import { ReactComponent as JiraProvider } from '@/images/integrations/jira.svg'
import { ReactComponent as GitHubProvider } from '@/images/integrations/github.svg'
+import { ReactComponent as TapdProvider } from '@/images/integrations/tapd.svg'
// import GitExtractorProvider from '@/images/git.png'
// import RefDiffProvider from '@/images/git-diff.png'
// import { ReactComponent as NullProvider } from '@/images/integrations/null.svg'
@@ -70,6 +71,24 @@
)
},
{
+ id: Providers.TAPD,
+ type: ProviderTypes.INTEGRATION,
+ enabled: true,
+ multiConnection: true,
+ name: ProviderLabels.TAPD,
+ icon: <TapdProvider className='providerIconSvg' width='30' height='30' style={{ float: 'left', marginTop: '5px' }} />,
+ iconDashboard: <TapdProvider className='providerIconSvg' width='40' height='40' />,
+ settings: ({ activeProvider, activeConnection, isSaving, isSavingConnection, setSettings }) => (
+ <JenkinsSettings
+ provider={activeProvider}
+ connection={activeConnection}
+ isSaving={isSaving}
+ isSavingConnection={isSavingConnection}
+ onSettingsChange={setSettings}
+ />
+ )
+ },
+ {
id: Providers.JIRA,
type: ProviderTypes.INTEGRATION,
enabled: true,
diff --git a/config-ui/src/hooks/useConnectionManager.jsx b/config-ui/src/hooks/useConnectionManager.jsx
index aa762d6..37e2c03 100644
--- a/config-ui/src/hooks/useConnectionManager.jsx
+++ b/config-ui/src/hooks/useConnectionManager.jsx
@@ -149,6 +149,16 @@
...connectionPayload,
}
break
+ case Providers.TAPD:
+ connectionPayload = {
+ name: name,
+ endpoint: endpointUrl,
+ username: username,
+ password: password,
+ proxy: proxy,
+ ...connectionPayload,
+ }
+ break
case Providers.GITHUB:
connectionPayload = {
name: name,
diff --git a/config-ui/src/hooks/usePipelineManager.jsx b/config-ui/src/hooks/usePipelineManager.jsx
index b2d7f8e..5f29e41 100644
--- a/config-ui/src/hooks/usePipelineManager.jsx
+++ b/config-ui/src/hooks/usePipelineManager.jsx
@@ -53,7 +53,8 @@
Providers.GITEXTRACTOR,
Providers.FEISHU,
Providers.AE,
- Providers.DBT
+ Providers.DBT,
+ Providers.TAPD
])
const PIPELINES_ENDPOINT = useMemo(() => `${DEVLAKE_ENDPOINT}/pipelines`, [DEVLAKE_ENDPOINT])
diff --git a/config-ui/src/hooks/usePipelineValidation.jsx b/config-ui/src/hooks/usePipelineValidation.jsx
index 43e8609..a8e10d7 100644
--- a/config-ui/src/hooks/usePipelineValidation.jsx
+++ b/config-ui/src/hooks/usePipelineValidation.jsx
@@ -56,7 +56,8 @@
Providers.FEISHU,
Providers.AE,
Providers.DBT,
- Providers.STARROCKS
+ Providers.STARROCKS,
+ Providers.TAPD
])
const clear = () => {
diff --git a/config-ui/src/images/integrations/tapd.svg b/config-ui/src/images/integrations/tapd.svg
new file mode 100644
index 0000000..3481500
--- /dev/null
+++ b/config-ui/src/images/integrations/tapd.svg
@@ -0,0 +1,6 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M30.1151 10.3491L27.9087 3.35706L4.95635 10.3491L6.96429 17.3253L15.0198 15.0396L19.4008 29.492L26.5992 27.246L22.1389 12.8491L30.1151 10.3491Z" fill="#7497F7"/>
+<path d="M2.25 33.6666H15.2659L2.25 12.0317V33.6666Z" fill="#7497F7"/>
+<path d="M33.0119 2.33325L27.2103 33.6666H33.75V2.33325H33.0119Z" fill="#7497F7"/>
+<path d="M2.25 2.33325V8.6904L23.6944 2.33325H2.25Z" fill="#7497F7"/>
+</svg>
diff --git a/config-ui/src/pages/blueprints/create-blueprint.jsx b/config-ui/src/pages/blueprints/create-blueprint.jsx
index df0aad2..560da91 100644
--- a/config-ui/src/pages/blueprints/create-blueprint.jsx
+++ b/config-ui/src/pages/blueprints/create-blueprint.jsx
@@ -494,6 +494,7 @@
switch (configuredConnection.provider) {
case Providers.GITLAB:
case Providers.JIRA:
+ case Providers.TAPD:
case Providers.GITHUB:
items = dataEntitiesList.filter((d) => d.name !== 'ci-cd')
break
@@ -550,6 +551,11 @@
...newScope,
}
break
+ case Providers.TAPD:
+ newScope = {
+ ...newScope,
+ }
+ break
case Providers.GITHUB:
newScope = projects[connection.id]?.map((p) => ({
...newScope,
@@ -905,6 +911,9 @@
case Providers.JENKINS:
// No Transform Settings...
break
+ case Providers.TAPD:
+ // No Transform Settings...
+ break
case Providers.GITLAB:
// No Transform Settings...
break
diff --git a/config-ui/src/pages/configure/connections/AddConnection.jsx b/config-ui/src/pages/configure/connections/AddConnection.jsx
index 7013609..4ad749b 100644
--- a/config-ui/src/pages/configure/connections/AddConnection.jsx
+++ b/config-ui/src/pages/configure/connections/AddConnection.jsx
@@ -112,6 +112,7 @@
case Providers.GITHUB:
case Providers.GITLAB:
case Providers.JIRA:
+ case Providers.TAPD:
default:
setName('')
break
@@ -184,7 +185,7 @@
allTestResponses={allTestResponses}
errors={errors}
showError={showError}
- authType={[Providers.JENKINS, Providers.JIRA].includes(activeProvider.id) ? 'plain' : 'token'}
+ authType={[Providers.JENKINS, Providers.JIRA, Providers.TAPD].includes(activeProvider.id) ? 'plain' : 'token'}
sourceLimits={ProviderConnectionLimits}
labels={ProviderFormLabels[activeProvider.id]}
placeholders={ProviderFormPlaceholders[activeProvider.id]}
diff --git a/config-ui/src/pages/configure/connections/ConfigureConnection.jsx b/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
index 5234f5c..bbc60ba 100644
--- a/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
+++ b/config-ui/src/pages/configure/connections/ConfigureConnection.jsx
@@ -216,7 +216,7 @@
</div>
{activeConnection && (
<>
- {[Providers.GITLAB, Providers.JIRA].includes(activeProvider.id) &&
+ {[Providers.GITLAB, Providers.JIRA, Providers.TAPD].includes(activeProvider.id) &&
(<h2 style={{ margin: 0 }}>#{activeConnection.ID} {activeConnection.name}</h2>)}
<p className='page-description'>Manage settings and options for this connection.</p>
</>
@@ -255,7 +255,7 @@
username={username}
password={password}
// JIRA and GITLAB are multi-connection plugins, for now we intentially won't include additional settings during save...
- onSave={() => saveConnection(![Providers.GITLAB, Providers.JIRA].includes(activeProvider.id) ? settings : {})}
+ onSave={() => saveConnection(![Providers.GITLAB, Providers.JIRA,Providers.TAPD].includes(activeProvider.id) ? settings : {})}
onTest={testConnection}
onCancel={cancel}
onValidate={validate}
@@ -272,7 +272,7 @@
allTestResponses={allTestResponses}
errors={errors}
showError={showConnectionError}
- authType={[Providers.JENKINS, Providers.JIRA].includes(activeProvider.id) ? 'plain' : 'token'}
+ authType={[Providers.JENKINS, Providers.JIRA, Providers.TAPD].includes(activeProvider.id) ? 'plain' : 'token'}
showLimitWarning={false}
sourceLimits={ProviderConnectionLimits}
labels={ProviderFormLabels[activeProvider.id]}
diff --git a/config-ui/src/pages/configure/connections/ConnectionForm.jsx b/config-ui/src/pages/configure/connections/ConnectionForm.jsx
index dc9cf9a..c929fa5 100644
--- a/config-ui/src/pages/configure/connections/ConnectionForm.jsx
+++ b/config-ui/src/pages/configure/connections/ConnectionForm.jsx
@@ -702,7 +702,7 @@
</div>
</>
)}
- {[Providers.GITHUB, Providers.GITLAB, Providers.JIRA].includes(
+ {[Providers.GITHUB, Providers.GITLAB, Providers.JIRA, Providers.TAPD].includes(
activeProvider.id
) && (
<div className='formContainer'>
diff --git a/config-ui/src/pages/configure/connections/EditConnection.jsx b/config-ui/src/pages/configure/connections/EditConnection.jsx
index 2631170..d1bfc37 100644
--- a/config-ui/src/pages/configure/connections/EditConnection.jsx
+++ b/config-ui/src/pages/configure/connections/EditConnection.jsx
@@ -76,6 +76,7 @@
setEndpointUrl(activeConnection.endpoint)
switch (activeProvider.id) {
case Providers.JENKINS:
+ case Providers.TAPD:
case Providers.JIRA:
setUsername(activeConnection.username)
setPassword(activeConnection.password)
@@ -155,7 +156,7 @@
testStatus={testStatus}
errors={errors}
showError={showError}
- authType={[Providers.JENKINS, Providers.JIRA].includes(activeProvider.id) ? 'plain' : 'token'}
+ authType={[Providers.JENKINS, Providers.JIRA, Providers.TAPD].includes(activeProvider.id) ? 'plain' : 'token'}
sourceLimits={ProviderConnectionLimits}
labels={ProviderFormLabels[activeProvider.id]}
placeholders={ProviderFormPlaceholders[activeProvider.id]}
diff --git a/config-ui/src/pages/pipelines/activity.jsx b/config-ui/src/pages/pipelines/activity.jsx
index c127e94..015244f 100644
--- a/config-ui/src/pages/pipelines/activity.jsx
+++ b/config-ui/src/pages/pipelines/activity.jsx
@@ -44,6 +44,7 @@
import CodeInspector from '@/components/pipelines/CodeInspector'
import { ReactComponent as GitlabProviderIcon } from '@/images/integrations/gitlab.svg'
import { ReactComponent as JenkinsProviderIcon } from '@/images/integrations/jenkins.svg'
+import { ReactComponent as TapdProviderIcon } from '@/images/integrations/tapd.svg'
import { ReactComponent as JiraProviderIcon } from '@/images/integrations/jira.svg'
import { ReactComponent as GitHubProviderIcon } from '@/images/integrations/github.svg'
import { ReactComponent as HelpIcon } from '@/images/help.svg'
@@ -507,6 +508,24 @@
</div>
</div>
)}
+ {pipelineHasProvider(Providers.TAPD) && (
+ <div className='jenkins-settings' style={{ display: 'flex' }}>
+ <div style={{ display: 'flex', padding: '2px 6px' }}>
+ <TapdProviderIcon width={24} height={24} />
+ </div>
+ <div>
+ <label style={{ lineHeight: '100%', display: 'block', fontSize: '10px', marginTop: '2px', marginBottom: '10px' }}>
+ <strong style={{
+ fontSize: '16px',
+ fontWeight: 800
+ }}
+ >{ProviderLabels.TAPD}
+ </strong><br />Auto-configured
+ </label>
+ <span style={{ color: Colors.GRAY3 }}>(No Settings)</span>
+ </div>
+ </div>
+ )}
{pipelineHasProvider(Providers.JIRA) && (
<div className='jira-settings' style={{ display: 'flex', paddingLeft: '20px' }}>
<div style={{ display: 'flex', padding: '2px 6px' }}>
diff --git a/plugins/tapd/api/connection.go b/plugins/tapd/api/connection.go
index ec1767c..138f3ce 100644
--- a/plugins/tapd/api/connection.go
+++ b/plugins/tapd/api/connection.go
@@ -36,38 +36,37 @@
*/
func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, error) {
// process input
- var params models.TestConnectionRequest
- err := mapstructure.Decode(input.Body, ¶ms)
+ var connection models.TestConnectionRequest
+ err := mapstructure.Decode(input.Body, &connection)
if err != nil {
return nil, err
}
- err = vld.Struct(params)
+ err = vld.Struct(connection)
if err != nil {
return nil, err
}
// verify multiple token in parallel
// PLEASE NOTE: This works because GitHub API Client rotates tokens on each request
- token := params.Auth
apiClient, err := helper.NewApiClient(
context.TODO(),
- params.Endpoint,
+ connection.Endpoint,
map[string]string{
- "Authorization": fmt.Sprintf("Basic %s", token),
+ "Authorization": fmt.Sprintf("Basic %s", connection.GetEncodedToken()),
},
3*time.Second,
- params.Proxy,
+ connection.Proxy,
basicRes,
)
if err != nil {
- return nil, fmt.Errorf("verify token failed for %s %w", token, err)
+ return nil, fmt.Errorf("verify token failed for %s %w", connection.Username, err)
}
res, err := apiClient.Get("/quickstart/testauth", nil, nil)
if err != nil {
return nil, err
}
if res.StatusCode == http.StatusUnauthorized {
- return nil, fmt.Errorf("verify token failed for %s", token)
+ return nil, fmt.Errorf("verify token failed for %s", connection.Username)
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
diff --git a/plugins/tapd/models/connection.go b/plugins/tapd/models/connection.go
index 5073988..c10f116 100644
--- a/plugins/tapd/models/connection.go
+++ b/plugins/tapd/models/connection.go
@@ -25,9 +25,9 @@
)
type TestConnectionRequest struct {
- Endpoint string `json:"endpoint" validate:"required,url"`
- Auth string `json:"auth" validate:"required"`
- Proxy string `json:"proxy" gorm:"type:varchar(255)"`
+ Endpoint string `json:"endpoint"`
+ Proxy string `json:"proxy"`
+ helper.BasicAuth `mapstructure:",squash"`
}
type WorkspaceResponse struct {
diff --git a/plugins/tapd/tasks/company_extractor.go b/plugins/tapd/tasks/company_extractor.go
index 97bdc67..be7b6fb 100644
--- a/plugins/tapd/tasks/company_extractor.go
+++ b/plugins/tapd/tasks/company_extractor.go
@@ -36,7 +36,7 @@
}
func ExtractCompanies(taskCtx core.SubTaskContext) error {
- rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_COMPANY_TABLE, false)
+ rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, RAW_COMPANY_TABLE, true)
extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{
RawDataSubTaskArgs: *rawDataSubTaskArgs,
Extract: func(row *helper.RawData) ([]interface{}, error) {