You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by wa...@apache.org on 2022/08/17 03:24:37 UTC
[incubator-devlake] branch release-v0.12 updated: fix: config-ui blueprints service pack 2.0 (#2630)
This is an automated email from the ASF dual-hosted git repository.
warren pushed a commit to branch release-v0.12
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/release-v0.12 by this push:
new 808255bf fix: config-ui blueprints service pack 2.0 (#2630)
808255bf is described below
commit 808255bf9b30c7dc83a542f6d02766fb5a744838
Author: Julien Chinapen <ju...@merico.dev>
AuthorDate: Wed Aug 3 02:51:54 2022 -0400
fix: config-ui blueprints service pack 2.0 (#2630)
* fix: enhance stage task name text details
* fix: cleanup mock objects from blueprint detail
* chore: apply linting cleanups
* feat: enable pipeline logfile download
* fix: resolve lint dependencies with bp validate
* fix: terminate regex match on repo name validation
---
config-ui/package-lock.json | 11 +
config-ui/package.json | 1 +
.../src/components/blueprints/BlueprintsGrid.jsx | 9 +-
.../components/blueprints/DataEntitiesSelector.jsx | 6 +-
.../blueprints/create-workflow/DataScopes.jsx | 4 +
.../create-workflow/DataTransformations.jsx | 2 +
.../src/components/pipelines/StageTaskName.jsx | 26 +-
config-ui/src/data/NullBlueprint.js | 2 +-
config-ui/src/data/Task.js | 66 +++
config-ui/src/data/TestBlueprintDetail.js | 379 ++++++++++++++
config-ui/src/hooks/useBlueprintValidation.jsx | 8 +-
config-ui/src/hooks/usePipelineManager.jsx | 23 +-
.../src/pages/blueprints/blueprint-detail.jsx | 583 ++-------------------
.../src/pages/blueprints/create-blueprint.jsx | 2 +
config-ui/src/pages/blueprints/index.jsx | 3 +
config-ui/src/pages/configure/settings/github.jsx | 2 +-
16 files changed, 570 insertions(+), 557 deletions(-)
diff --git a/config-ui/package-lock.json b/config-ui/package-lock.json
index 476d942c..22f3cf35 100644
--- a/config-ui/package-lock.json
+++ b/config-ui/package-lock.json
@@ -21,6 +21,7 @@
"dayjs": "^1.10.7",
"dotenv": "^10.0.0",
"dotenv-webpack": "^7.0.3",
+ "file-saver": "^2.0.5",
"jetbrains-mono": "^1.0.6",
"react": "17.0.2",
"react-dom": "17.0.2",
@@ -10512,6 +10513,11 @@
"webpack": "^4.0.0 || ^5.0.0"
}
},
+ "node_modules/file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+ },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -33835,6 +33841,11 @@
"schema-utils": "^3.0.0"
}
},
+ "file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+ },
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
diff --git a/config-ui/package.json b/config-ui/package.json
index 235469bd..45d8b0bc 100644
--- a/config-ui/package.json
+++ b/config-ui/package.json
@@ -24,6 +24,7 @@
"dayjs": "^1.10.7",
"dotenv": "^10.0.0",
"dotenv-webpack": "^7.0.3",
+ "file-saver": "^2.0.5",
"jetbrains-mono": "^1.0.6",
"react": "17.0.2",
"react-dom": "17.0.2",
diff --git a/config-ui/src/components/blueprints/BlueprintsGrid.jsx b/config-ui/src/components/blueprints/BlueprintsGrid.jsx
index aa897be2..57048a4f 100644
--- a/config-ui/src/components/blueprints/BlueprintsGrid.jsx
+++ b/config-ui/src/components/blueprints/BlueprintsGrid.jsx
@@ -113,6 +113,13 @@ const BlueprintsGrid = (props) => {
>
Monthly
</Button>
+ <Button
+ intent={activeFilterStatus === 'manual' ? Intent.PRIMARY : Intent.NONE}
+ active={activeFilterStatus === 'manual'}
+ onClick={() => onFilter('manual')}
+ >
+ Manual
+ </Button>
<Button
intent={activeFilterStatus === 'custom' ? Intent.PRIMARY : Intent.NONE}
active={activeFilterStatus === 'custom'}
@@ -304,7 +311,7 @@ const BlueprintsGrid = (props) => {
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '0', padding: '10px' }}>
- <div style={{ letterSpacing: '1px', fontWeight: 800 }}>
+ <div style={{ letterSpacing: '1px', fontWeight: 800, whiteSpace: 'nowrap' }}>
<Icon icon='bold' color={Colors.BLUE4} size={14} style={{ marginRight: '5px' }} /> BLUEPRINT ID {activeBlueprint?.id}
{isLoading && (
<span style={{ paddingLeft: '20px', fontWeight: 700, color: '#777777', fontSize: '11px' }}>
diff --git a/config-ui/src/components/blueprints/DataEntitiesSelector.jsx b/config-ui/src/components/blueprints/DataEntitiesSelector.jsx
index 68f9cac3..bc20eddc 100644
--- a/config-ui/src/components/blueprints/DataEntitiesSelector.jsx
+++ b/config-ui/src/components/blueprints/DataEntitiesSelector.jsx
@@ -41,6 +41,8 @@ import {
Tag,
} from '@blueprintjs/core'
import { MultiSelect, Select } from '@blueprintjs/select'
+import InputValidationError from '@/components/validation/InputValidationError'
+
const DataEntitiesSelector = (props) => {
const {
connections = [],
@@ -55,6 +57,8 @@ const DataEntitiesSelector = (props) => {
onItemSelect = () => {},
onRemove = () => {},
onClear = () => {},
+ fieldHasError = () => {},
+ getFieldError = () => {},
itemRenderer = (item, { handleClick, modifiers }) => (
<MenuItem
active={modifiers.active || selectedItems.find(i => i.id === item.id)}
@@ -101,7 +105,7 @@ const DataEntitiesSelector = (props) => {
resetOnSelect={true}
placeholder={placeholder}
popoverProps={{ usePortal: false, minimal: true }}
- className='multiselector-connections'
+ className='multiselector-entities'
inline={true}
fill={true}
items={items}
diff --git a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
index a8d8fc06..24afeb6e 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataScopes.jsx
@@ -56,6 +56,8 @@ const DataScopes = (props) => {
setProjects = () => {},
setBoards = () => {},
prevStep = () => {},
+ fieldHasError = () => {},
+ getFieldError = () => {},
isSaving = false,
isRunning = false,
} = props
@@ -186,6 +188,8 @@ const DataScopes = (props) => {
// restrictedItems={getRestrictedDataEntities()}
onItemSelect={setDataEntities}
onClear={setDataEntities}
+ fieldHasError={fieldHasError}
+ getFieldError={getFieldError}
onRemove={setDataEntities}
disabled={isSaving}
configuredConnection={configuredConnection}
diff --git a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
index 05194e06..c4641b54 100644
--- a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
+++ b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx
@@ -70,6 +70,8 @@ const DataTransformations = (props) => {
onSave = () => {},
onCancel = () => {},
onClear = () => {},
+ fieldHasError = () => {},
+ getFieldError = () => {},
isSaving = false,
isSavingConnection = false,
isRunning = false,
diff --git a/config-ui/src/components/pipelines/StageTaskName.jsx b/config-ui/src/components/pipelines/StageTaskName.jsx
index a5eb3c1f..80610392 100644
--- a/config-ui/src/components/pipelines/StageTaskName.jsx
+++ b/config-ui/src/components/pipelines/StageTaskName.jsx
@@ -53,15 +53,21 @@ const StageTaskName = (props) => {
<Popover
className='trigger-pipeline-activity-help'
popoverClassName='popover-help-pipeline-activity'
- // isOpen={showDetails && showDetails.ID === task.ID}
+ // isOpen={false}
onClosed={onClose}
position={Position.RIGHT}
autoFocus={false}
enforceFocus={false}
usePortal={true}
- disabled
+ // disabled
>
- <span className='task-plugin-text' ref={popoverTriggerRef} style={{ display: 'block', margin: '5px 0 5px 0' }}><strong>Task ID {task.id}</strong> {' '} {ProviderLabels[task?.plugin?.toUpperCase()]}</span>
+ <span className='task-plugin-text' ref={popoverTriggerRef} style={{ display: 'block', margin: '5px 0 5px 0' }}>
+ <strong>Task ID {task.id}</strong> {' '} {ProviderLabels[task?.plugin?.toUpperCase()]}{' '}
+ {task.plugin === Providers.GITHUB && task.plugin !== Providers.JENKINS && (<>@{task.options.owner}/{task.options.repo}</>)}
+ {task.plugin === Providers.JIRA && (<>Board ID {task.options.boardId}</>)}
+ {task.plugin === Providers.GITLAB && (<>Project ID {task.options.projectId}</>)}
+ {task.plugin === Providers.GITEXTRACTOR && (<>{task.options.repoId}</>)}
+ </span>
<>
<div style={{ textShadow: 'none', fontSize: '12px', padding: '12px', maxWidth: '400px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
@@ -86,8 +92,9 @@ const StageTaskName = (props) => {
{task.plugin === Providers.GITEXTRACTOR && (<>{ProviderLabels.GITEXTRACTOR}</>)}
{task.plugin === Providers.FEISHU && (<>{ProviderLabels.FEISHU}</>)}
{task.plugin === Providers.JENKINS && (<>{ProviderLabels.JENKINS}</>)}
- {(task.plugin === Providers.GITLAB || task.plugin === Providers.JIRA) && (<>ID {task.options.projectId || task.options.boardId}</>)}
- {task.plugin === Providers.GITHUB && task.plugin !== Providers.JENKINS && (<>@{task.options.owner}/{task.options.repositoryName}</>)}
+ {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}</>)}
</H3>
{![Providers.JENKINS, Providers.REFDIFF, Providers.GITEXTRACTOR].includes(task.plugin) && (
<>{ProviderLabels[task.plugin?.toUpperCase()] || 'System Task'}<br /></>
@@ -107,15 +114,14 @@ const StageTaskName = (props) => {
{Number(task.status === 'TASK_COMPLETED' ? 100 : (task.progress / 1) * 100).toFixed(0)}%
</div>
<div style={{ padding: '0 0 10px 20px' }}>
- {ProviderIcons[task.plugin.toLowerCase()](32, 32)}
+ {ProviderIcons[task.plugin?.toLowerCase()] ? ProviderIcons[task.plugin?.toLowerCase()](24, 24) : null}
</div>
</div>
{task.status === 'TASK_CREATED' && (
<div style={{ fontSize: '10px' }}>
- <p style={{ fontSize: '14px' }}>
- This task (ID #{task.ID}) is <strong>PENDING</strong> and has not yet started.
+ <p style={{ fontSize: '12px' }}>
+ Task #{task.id} is <strong>pending</strong> and has not yet started.
</p>
- <strong>Created Date —</strong> <span>{dayjs(task.CreatedAt).format('L LT')}</span>
</div>
)}
{task.status !== 'TASK_CREATED' && (
@@ -123,7 +129,7 @@ const StageTaskName = (props) => {
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
<div>
<label style={{ color: Colors.GRAY2 }}>ID</label><br />
- <span>{task.ID}</span>
+ <span>{task.id}</span>
</div>
<div style={{ marginLeft: '20px' }}>
<label style={{ color: Colors.GRAY2 }}>Created</label><br />
diff --git a/config-ui/src/data/NullBlueprint.js b/config-ui/src/data/NullBlueprint.js
index f40aa2b2..9f544c0d 100644
--- a/config-ui/src/data/NullBlueprint.js
+++ b/config-ui/src/data/NullBlueprint.js
@@ -46,7 +46,7 @@ const NullBlueprint = {
cronConfig: '0 0 * * *',
description: '',
interval: 'daily',
- enabled: BlueprintStatus.DISABLED,
+ enable: BlueprintStatus.DISABLED,
mode: BlueprintMode.NORMAL,
isManual: false
}
diff --git a/config-ui/src/data/Task.js b/config-ui/src/data/Task.js
new file mode 100644
index 00000000..0564f743
--- /dev/null
+++ b/config-ui/src/data/Task.js
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ */
+
+const StageStatus = {
+ PENDING: 'Pending',
+ COMPLETE: 'Complete',
+ FAILED: 'Failed',
+ ACTIVE: 'In Progress',
+}
+
+const TaskStatus = {
+ COMPLETE: 'TASK_COMPLETED',
+ FAILED: 'TASK_FAILED',
+ ACTIVE: 'TASK_RUNNING',
+ RUNNING: 'TASK_RUNNING',
+ CREATED: 'TASK_CREATED',
+ PENDING: 'TASK_CREATED',
+ CANCELLED: 'TASK_CANCELLED',
+}
+
+const TaskStatusLabels = {
+ [TaskStatus.COMPLETE]: 'Succeeded',
+ [TaskStatus.FAILED]: 'Failed',
+ [TaskStatus.ACTIVE]: 'In Progress',
+ [TaskStatus.RUNNING]: 'In Progress',
+ [TaskStatus.CREATED]: 'Created (Pending)',
+ [TaskStatus.PENDING]: 'Created (Pending)',
+ [TaskStatus.CANCELLED]: 'Cancelled',
+}
+
+const StatusColors = {
+ PENDING: '#292B3F',
+ COMPLETE: '#4DB764',
+ FAILED: '#E34040',
+ ACTIVE: '#7497F7',
+}
+
+const StatusBgColors = {
+ PENDING: 'transparent',
+ COMPLETE: '#EDFBF0',
+ FAILED: '#FEEFEF',
+ ACTIVE: '#F0F4FE',
+}
+
+export {
+ StageStatus,
+ TaskStatus,
+ TaskStatusLabels,
+ StatusColors,
+ StatusBgColors
+}
diff --git a/config-ui/src/data/TestBlueprintDetail.js b/config-ui/src/data/TestBlueprintDetail.js
new file mode 100644
index 00000000..451943fe
--- /dev/null
+++ b/config-ui/src/data/TestBlueprintDetail.js
@@ -0,0 +1,379 @@
+/*
+ * 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 {
+ Intent,
+ Icon,
+ Colors,
+ Spinner
+} from '@blueprintjs/core'
+
+import { NullBlueprint } from '@/data/NullBlueprint'
+import { Providers, ProviderIcons } from '@/data/Providers'
+import { StageStatus, TaskStatus, TaskStatusLabels, StatusColors, StatusBgColors } from '@/data/Task'
+
+const EMPTY_RUN = {
+ id: null,
+ status: TaskStatus.CREATED,
+ statusLabel: TaskStatusLabels[TaskStatus.RUNNING],
+ icon: null,
+ startedAt: Date.now(),
+ duration: '0 min',
+ stage: 'Stage 1',
+ tasksTotal: 0,
+ tasksFinished: 0,
+ error: null,
+}
+
+const TEST_BLUEPRINT = {
+ ...NullBlueprint,
+ id: 1,
+ name: 'DevLake Daily Blueprint',
+ createdAt: new Date().toLocaleString(),
+ updatedAt: new Date().toLocaleString(),
+}
+
+const TEST_CONNECTIONS = [
+ {
+ id: 0,
+ provider: Providers.GITHUB,
+ name: 'Merico GitHub',
+ dataScope: 'merico-dev/ake, merico-dev/lake-website',
+ dataEntities: ['code', 'ticket', 'user'],
+ },
+ {
+ id: 0,
+ provider: Providers.JIRA,
+ name: 'Merico JIRA',
+ dataScope: 'Sprint Dev Board, DevLake Sync Board ',
+ dataEntities: ['ticket'],
+ },
+]
+
+// eslint-disable-next-line no-unused-vars
+const TEST_BLUEPRINT_API_RESPONSE = {
+ name: 'DEVLAKE (Hourly)',
+ mode: 'NORMAL',
+ plan: [
+ [
+ {
+ plugin: 'github',
+ subtasks: [
+ 'collectApiRepo',
+ 'extractApiRepo',
+ 'collectApiIssues',
+ 'extractApiIssues',
+ 'collectApiPullRequests',
+ 'extractApiPullRequests',
+ 'collectApiComments',
+ 'extractApiComments',
+ 'collectApiEvents',
+ 'extractApiEvents',
+ 'collectApiPullRequestCommits',
+ 'extractApiPullRequestCommits',
+ 'collectApiPullRequestReviews',
+ 'extractApiPullRequestReviewers',
+ 'collectApiCommits',
+ 'extractApiCommits',
+ 'collectApiCommitStats',
+ 'extractApiCommitStats',
+ 'enrichPullRequestIssues',
+ 'convertRepo',
+ 'convertIssues',
+ 'convertCommits',
+ 'convertIssueLabels',
+ 'convertPullRequestCommits',
+ 'convertPullRequests',
+ 'convertPullRequestLabels',
+ 'convertPullRequestIssues',
+ 'convertIssueComments',
+ 'convertPullRequestComments',
+ ],
+ options: {
+ connectionId: 1,
+ owner: 'e2corporation',
+ repo: 'incubator-devlake',
+ transformationRules: {
+ issueComponent: '',
+ issuePriority: '',
+ issueSeverity: '',
+ issueTypeBug: '',
+ issueTypeIncident: '',
+ issueTypeRequirement: '',
+ prComponent: '',
+ prType: '',
+ },
+ },
+ },
+ {
+ plugin: 'gitextractor',
+ subtasks: null,
+ options: {
+ repoId: 'github:GithubRepo:1:506830252',
+ url: 'https://git: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@github.com/e2corporation/incubator-devlake.git',
+ },
+ },
+ ],
+ [
+ {
+ plugin: 'refdiff',
+ subtasks: null,
+ options: {
+ tagsLimit: 10,
+ tagsOrder: '',
+ tagsPattern: '',
+ },
+ },
+ ],
+ ],
+ enable: true,
+ cronConfig: '0 0 * * *',
+ isManual: false,
+ settings: {
+ version: '1.0.0',
+ connections: [
+ {
+ connectionId: 1,
+ plugin: 'github',
+ scope: [
+ {
+ entities: ['CODE', 'TICKET'],
+ options: {
+ owner: 'e2corporation',
+ repo: 'incubator-devlake',
+ },
+ transformation: {
+ prType: '',
+ prComponent: '',
+ issueSeverity: '',
+ issueComponent: '',
+ issuePriority: '',
+ issueTypeRequirement: '',
+ issueTypeBug: '',
+ issueTypeIncident: '',
+ refdiff: {
+ tagsOrder: '',
+ tagsPattern: '',
+ tagsLimit: 10,
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ id: 1,
+ createdAt: '2022-07-11T10:23:38.908-04:00',
+ updatedAt: '2022-07-11T10:23:38.908-04:00',
+}
+
+const TEST_STAGES = [
+ {
+ id: 1,
+ name: 'stage-1',
+ title: 'Stage 1',
+ status: StageStatus.COMPLETED,
+ icon: <Icon icon='tick-circle' size={14} color={StatusColors.COMPLETE} />,
+ tasks: [
+ {
+ id: 0,
+ provider: 'jira',
+ icon: ProviderIcons[Providers.JIRA](14, 14),
+ title: 'JIRA',
+ caption: 'STREAM Board',
+ duration: '4 min',
+ subTasksCompleted: 25,
+ recordsFinished: 1234,
+ message: 'All 25 subtasks completed',
+ status: TaskStatus.COMPLETE,
+ },
+ {
+ id: 0,
+ provider: 'jira',
+ icon: ProviderIcons[Providers.JIRA](14, 14),
+ title: 'JIRA',
+ caption: 'LAKE Board',
+ duration: '4 min',
+ subTasksCompleted: 25,
+ recordsFinished: 1234,
+ message: 'All 25 subtasks completed',
+ status: TaskStatus.COMPLETE,
+ },
+ ],
+ stageHeaderClassName: 'complete',
+ },
+ {
+ id: 2,
+ name: 'stage-2',
+ title: 'Stage 2',
+ status: StageStatus.PENDING,
+ icon: <Spinner size={14} intent={Intent.PRIMARY} />,
+ tasks: [
+ {
+ id: 0,
+ provider: 'jira',
+ icon: ProviderIcons[Providers.JIRA](14, 14),
+ title: 'JIRA',
+ caption: 'EE Board',
+ duration: '5 min',
+ subTasksCompleted: 25,
+ recordsFinished: 1234,
+ message: 'Subtask 5/25: Extracting Issues',
+ status: TaskStatus.ACTIVE,
+ },
+ {
+ id: 0,
+ provider: 'jira',
+ icon: ProviderIcons[Providers.JIRA](14, 14),
+ title: 'JIRA',
+ caption: 'EE Bugs Board',
+ duration: '0 min',
+ subTasksCompleted: 0,
+ recordsFinished: 0,
+ message: 'Invalid Board ID',
+ status: TaskStatus.FAILED,
+ },
+ ],
+ stageHeaderClassName: 'active',
+ },
+ {
+ id: 3,
+ name: 'stage-3',
+ title: 'Stage 3',
+ status: StageStatus.PENDING,
+ icon: null,
+ tasks: [
+ {
+ id: 0,
+ provider: 'github',
+ icon: ProviderIcons[Providers.GITHUB](14, 14),
+ title: 'GITHUB',
+ caption: 'merico-dev/lake',
+ duration: null,
+ subTasksCompleted: 0,
+ recordsFinished: 0,
+ message: 'Subtasks pending',
+ status: TaskStatus.CREATED,
+ },
+ ],
+ stageHeaderClassName: 'pending',
+ },
+ {
+ id: 4,
+ name: 'stage-4',
+ title: 'Stage 4',
+ status: StageStatus.PENDING,
+ icon: null,
+ tasks: [
+ {
+ id: 0,
+ providr: 'github',
+ icon: ProviderIcons[Providers.GITHUB](14, 14),
+ title: 'GITHUB',
+ caption: 'merico-dev/lake',
+ duration: null,
+ subTasksCompleted: 0,
+ recordsFinished: 0,
+ message: 'Subtasks pending',
+ status: TaskStatus.CREATED,
+ },
+ ],
+ stageHeaderClassName: 'pending',
+ },
+]
+
+const TEST_HISTORICAL_RUNS = [
+ {
+ id: 0,
+ status: 'TASK_COMPLETED',
+ statusLabel: 'Completed',
+ statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:15 AM',
+ duration: '15 min',
+ },
+ {
+ id: 1,
+ status: 'TASK_COMPLETED',
+ statusLabel: 'Completed',
+ statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:15 AM',
+ duration: '15 min',
+ },
+ {
+ id: 2,
+ status: 'TASK_FAILED',
+ statusLabel: 'Failed',
+ statusIcon: <Icon icon='delete' size={14} color={Colors.RED5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:00 AM',
+ duration: '0 min',
+ },
+ {
+ id: 3,
+ status: 'TASK_COMPLETED',
+ statusLabel: 'Completed',
+ statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:15 AM',
+ duration: '15 min',
+ },
+ {
+ id: 4,
+ status: 'TASK_COMPLETED',
+ statusLabel: 'Completed',
+ statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:15 AM',
+ duration: '15 min',
+ },
+ {
+ id: 5,
+ status: 'TASK_FAILED',
+ statusLabel: 'Failed',
+ statusIcon: <Icon icon='delete' size={14} color={Colors.RED5} />,
+ startedAt: '05/25/2022 0:00 AM',
+ completedAt: '05/25/2022 0:00 AM',
+ duration: '0 min',
+ },
+]
+
+const TEST_RUN = {
+ id: null,
+ status: TaskStatus.RUNNING,
+ statusLabel: TaskStatusLabels[TaskStatus.RUNNING],
+ icon: <Spinner size={18} intent={Intent.PRIMARY} />,
+ startedAt: '7/7/2022, 5:31:33 PM',
+ duration: '1 min',
+ stage: 'Stage 1',
+ tasksTotal: 5,
+ tasksFinished: 8,
+ // totalTasks: 13,
+ error: null,
+}
+
+export {
+ EMPTY_RUN,
+ TEST_RUN,
+ TEST_BLUEPRINT,
+ TEST_CONNECTIONS,
+ TEST_HISTORICAL_RUNS,
+ TEST_BLUEPRINT_API_RESPONSE,
+ TEST_STAGES
+}
diff --git a/config-ui/src/hooks/useBlueprintValidation.jsx b/config-ui/src/hooks/useBlueprintValidation.jsx
index 780dc0eb..003d23db 100644
--- a/config-ui/src/hooks/useBlueprintValidation.jsx
+++ b/config-ui/src/hooks/useBlueprintValidation.jsx
@@ -58,7 +58,7 @@ function useBlueprintValidation ({
}, [])
const validateRepositoryName = useCallback((set = []) => {
- const repoRegExp = /([a-z0-9_-]){2,}\/([a-z0-9_-]){2,}/gi
+ const repoRegExp = /([a-z0-9_-]){2,}\/([a-z0-9_-]){2,}$/gi
return set.every(i => i.match(repoRegExp))
}, [])
@@ -68,7 +68,6 @@ function useBlueprintValidation ({
const validate = useCallback(() => {
const errs = []
- // console.log('>> VALIDATING BLUEPRINT ', name)
if (!name) {
errs.push('Blueprint Name: Enter a valid Name')
@@ -164,7 +163,10 @@ function useBlueprintValidation ({
projects,
activeStep,
activeProvider?.id,
- activeConnection
+ activeConnection,
+ isValidCronExpression,
+ validateNumericSet,
+ validateRepositoryName
])
const fieldHasError = useCallback((fieldId) => {
diff --git a/config-ui/src/hooks/usePipelineManager.jsx b/config-ui/src/hooks/usePipelineManager.jsx
index 8465966b..b2d7f8e0 100644
--- a/config-ui/src/hooks/usePipelineManager.jsx
+++ b/config-ui/src/hooks/usePipelineManager.jsx
@@ -15,7 +15,7 @@
* limitations under the License.
*
*/
-import { useState, useEffect, useCallback } from 'react'
+import { useState, useEffect, useCallback, useMemo } from 'react'
import { DEVLAKE_ENDPOINT } from '@/utils/config'
import request from '@/utils/request'
import { NullPipelineRun } from '@/data/NullPipelineRun'
@@ -56,6 +56,9 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
Providers.DBT
])
+ const PIPELINES_ENDPOINT = useMemo(() => `${DEVLAKE_ENDPOINT}/pipelines`, [DEVLAKE_ENDPOINT])
+ const [logfile, setLogfile] = useState('logging.tar.gz')
+
const runPipeline = useCallback((runSettings = null) => {
console.log('>> RUNNING PIPELINE....')
try {
@@ -64,6 +67,7 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
ToastNotification.clear()
console.log('>> DISPATCHING PIPELINE REQUEST', runSettings || settings)
const run = async () => {
+ // @todo: remove "ID" fallback key when no longer needed
const p = await request.post(`${DEVLAKE_ENDPOINT}/pipelines`, runSettings || settings)
const t = await request.get(`${DEVLAKE_ENDPOINT}/pipelines/${p.data?.ID || p.data?.id}/tasks`)
console.log('>> RAW PIPELINE DATA FROM API...', p.data)
@@ -122,12 +126,11 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
console.log('>> RAW PIPELINE TASKS DATA FROM API...', t.data)
setActivePipeline({
...p.data,
- ID: p.data.ID || p.data.id,
+ id: p.data.id,
tasks: [...t.data.tasks]
})
setPipelineRun((pR) => refresh ? { ...p.data, ID: p.data.id, tasks: [...t.data.tasks] } : pR)
- setLastRunId((lrId) => refresh ? p.data?.ID : lrId)
- // ToastNotification.show({ message: `Fetched Pipeline ID - ${p.data?.ID}.`, intent: 'danger', icon: 'small-tick' })
+ setLastRunId((lrId) => refresh ? p.data?.id : lrId)
setTimeout(() => {
setIsFetching(false)
}, 500)
@@ -167,10 +170,10 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
const p = await request.get(`${DEVLAKE_ENDPOINT}/pipelines${queryParams}`)
console.log('>> RAW PIPELINES RUN DATA FROM API...', p.data?.pipelines)
let pipelines = p.data && p.data.pipelines ? [...p.data.pipelines] : []
- pipelines = pipelines.map(p => ({ ...p, ID: p.ID || p.id }))
+ // @todo: remove "ID" fallback key when no longer needed
+ pipelines = pipelines.map(p => ({ ...p, ID: p.id }))
setPipelines(pipelines)
setPipelineCount(p.data ? p.data.count : 0)
- // ToastNotification.show({ message: `Fetched All Pipelines`, intent: 'danger', icon: 'small-tick' })
setTimeout(() => {
setIsFetchingAll(false)
}, fetchTimeout)
@@ -211,6 +214,10 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
}, [pipelineName, initialTasks])
+ const getPipelineLogfile = useCallback((pipelineId = 0) => {
+ return `${PIPELINES_ENDPOINT}/${pipelineId}/${logfile}`
+ }, [PIPELINES_ENDPOINT, logfile])
+
return {
errors,
isRunning,
@@ -226,6 +233,7 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
pipelines,
pipelineCount,
lastRunId,
+ logfile,
runPipeline,
cancelPipeline,
fetchPipeline,
@@ -234,7 +242,8 @@ function usePipelineManager (myPipelineName = `COLLECTION ${Date.now()}`, initia
buildPipelineStages,
detectPipelineProviders,
allowedProviders,
- setAllowedProviders
+ setAllowedProviders,
+ getPipelineLogfile
}
}
diff --git a/config-ui/src/pages/blueprints/blueprint-detail.jsx b/config-ui/src/pages/blueprints/blueprint-detail.jsx
index d5fe2abc..f485d5c4 100644
--- a/config-ui/src/pages/blueprints/blueprint-detail.jsx
+++ b/config-ui/src/pages/blueprints/blueprint-detail.jsx
@@ -21,6 +21,7 @@ import { useParams, useHistory } from 'react-router-dom'
import { DEVLAKE_ENDPOINT } from '@/utils/config'
import request from '@/utils/request'
import dayjs from '@/utils/time'
+import { saveAs } from 'file-saver'
import {
Button,
Elevation,
@@ -41,412 +42,18 @@ import {
import { NullBlueprint } from '@/data/NullBlueprint'
import { NullPipelineRun } from '@/data/NullPipelineRun'
import { Providers, ProviderLabels, ProviderIcons } from '@/data/Providers'
-
-// import {
-// WorkflowSteps,
-// WorkflowAdvancedSteps,
-// DEFAULT_DATA_ENTITIES,
-// DEFAULT_BOARDS,
-// } from '@/data/BlueprintWorkflow'
+import { StageStatus, TaskStatus, TaskStatusLabels, StatusColors, StatusBgColors } from '@/data/Task'
import Nav from '@/components/Nav'
import Sidebar from '@/components/Sidebar'
import Content from '@/components/Content'
-import TaskActivity from '@/components/pipelines/TaskActivity'
+// import TaskActivity from '@/components/pipelines/TaskActivity'
import CodeInspector from '@/components/pipelines/CodeInspector'
import StageLane from '@/components/pipelines/StageLane'
+import { ToastNotification } from '@/components/Toast'
import useBlueprintManager from '@/hooks/useBlueprintManager'
import usePipelineManager from '@/hooks/usePipelineManager'
-// import useConnectionManager from '@/hooks/useConnectionManager'
-// import { DataEntityTypes } from '@/data/DataEntities'
-
-const StageStatus = {
- PENDING: 'Pending',
- COMPLETE: 'Complete',
- FAILED: 'Failed',
- ACTIVE: 'In Progress',
-}
-
-const TaskStatus = {
- COMPLETE: 'TASK_COMPLETED',
- FAILED: 'TASK_FAILED',
- ACTIVE: 'TASK_RUNNING',
- RUNNING: 'TASK_RUNNING',
- CREATED: 'TASK_CREATED',
- PENDING: 'TASK_CREATED',
- CANCELLED: 'TASK_CANCELLED',
-}
-
-const TaskStatusLabels = {
- [TaskStatus.COMPLETE]: 'Succeeded',
- [TaskStatus.FAILED]: 'Failed',
- [TaskStatus.ACTIVE]: 'In Progress',
- [TaskStatus.RUNNING]: 'In Progress',
- [TaskStatus.CREATED]: 'Created (Pending)',
- [TaskStatus.PENDING]: 'Created (Pending)',
- [TaskStatus.CANCELLED]: 'Cancelled',
-}
-
-const StatusColors = {
- PENDING: '#292B3F',
- COMPLETE: '#4DB764',
- FAILED: '#E34040',
- ACTIVE: '#7497F7',
-}
-
-// eslint-disable-next-line no-unused-vars
-const StatusBgColors = {
- PENDING: 'transparent',
- COMPLETE: '#EDFBF0',
- FAILED: '#FEEFEF',
- ACTIVE: '#F0F4FE',
-}
-
-// eslint-disable-next-line no-unused-vars
-const TEST_BLUEPRINT = {
- ...NullBlueprint,
- id: 1,
- name: 'DevLake Daily Blueprint',
- createdAt: new Date().toLocaleString(),
- updatedAt: new Date().toLocaleString(),
-}
-
-// eslint-disable-next-line no-unused-vars
-const TEST_CONNECTIONS = [
- {
- id: 0,
- provider: Providers.GITHUB,
- name: 'Merico GitHub',
- dataScope: 'merico-dev/ake, merico-dev/lake-website',
- dataEntities: ['code', 'ticket', 'user'],
- },
- {
- id: 0,
- provider: Providers.JIRA,
- name: 'Merico JIRA',
- dataScope: 'Sprint Dev Board, DevLake Sync Board ',
- dataEntities: ['ticket'],
- },
-]
-
-const TEST_RUN = {
- id: null,
- status: TaskStatus.RUNNING,
- statusLabel: TaskStatusLabels[TaskStatus.RUNNING],
- icon: <Spinner size={18} intent={Intent.PRIMARY} />,
- startedAt: '7/7/2022, 5:31:33 PM',
- duration: '1 min',
- stage: 'Stage 1',
- tasksTotal: 5,
- tasksFinished: 8,
- // totalTasks: 13,
- error: null,
-}
-
-// eslint-disable-next-line no-unused-vars
-const EMPTY_RUN = {
- id: null,
- status: TaskStatus.CREATED,
- statusLabel: TaskStatusLabels[TaskStatus.RUNNING],
- icon: null,
- startedAt: Date.now(),
- duration: '0 min',
- stage: 'Stage 1',
- tasksTotal: 0,
- tasksFinished: 0,
- // totalTasks: 0,
- error: null,
-}
-
-// eslint-disable-next-line no-unused-vars
-const TEST_BLUEPRINT_API_RESPONSE = {
- name: 'DEVLAKE (Hourly)',
- mode: 'NORMAL',
- plan: [
- [
- {
- plugin: 'github',
- subtasks: [
- 'collectApiRepo',
- 'extractApiRepo',
- 'collectApiIssues',
- 'extractApiIssues',
- 'collectApiPullRequests',
- 'extractApiPullRequests',
- 'collectApiComments',
- 'extractApiComments',
- 'collectApiEvents',
- 'extractApiEvents',
- 'collectApiPullRequestCommits',
- 'extractApiPullRequestCommits',
- 'collectApiPullRequestReviews',
- 'extractApiPullRequestReviewers',
- 'collectApiCommits',
- 'extractApiCommits',
- 'collectApiCommitStats',
- 'extractApiCommitStats',
- 'enrichPullRequestIssues',
- 'convertRepo',
- 'convertIssues',
- 'convertCommits',
- 'convertIssueLabels',
- 'convertPullRequestCommits',
- 'convertPullRequests',
- 'convertPullRequestLabels',
- 'convertPullRequestIssues',
- 'convertIssueComments',
- 'convertPullRequestComments',
- ],
- options: {
- connectionId: 1,
- owner: 'e2corporation',
- repo: 'incubator-devlake',
- transformationRules: {
- issueComponent: '',
- issuePriority: '',
- issueSeverity: '',
- issueTypeBug: '',
- issueTypeIncident: '',
- issueTypeRequirement: '',
- prComponent: '',
- prType: '',
- },
- },
- },
- {
- plugin: 'gitextractor',
- subtasks: null,
- options: {
- repoId: 'github:GithubRepo:1:506830252',
- url: 'https://git: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@github.com/e2corporation/incubator-devlake.git',
- },
- },
- ],
- [
- {
- plugin: 'refdiff',
- subtasks: null,
- options: {
- tagsLimit: 10,
- tagsOrder: '',
- tagsPattern: '',
- },
- },
- ],
- ],
- enable: true,
- cronConfig: '0 0 * * *',
- isManual: false,
- settings: {
- version: '1.0.0',
- connections: [
- {
- connectionId: 1,
- plugin: 'github',
- scope: [
- {
- entities: ['CODE', 'TICKET'],
- options: {
- owner: 'e2corporation',
- repo: 'incubator-devlake',
- },
- transformation: {
- prType: '',
- prComponent: '',
- issueSeverity: '',
- issueComponent: '',
- issuePriority: '',
- issueTypeRequirement: '',
- issueTypeBug: '',
- issueTypeIncident: '',
- refdiff: {
- tagsOrder: '',
- tagsPattern: '',
- tagsLimit: 10,
- },
- },
- },
- ],
- },
- ],
- },
- id: 1,
- createdAt: '2022-07-11T10:23:38.908-04:00',
- updatedAt: '2022-07-11T10:23:38.908-04:00',
-}
-
-const TEST_STAGES = [
- {
- id: 1,
- name: 'stage-1',
- title: 'Stage 1',
- status: StageStatus.COMPLETED,
- icon: <Icon icon='tick-circle' size={14} color={StatusColors.COMPLETE} />,
- tasks: [
- {
- id: 0,
- provider: 'jira',
- icon: ProviderIcons[Providers.JIRA](14, 14),
- title: 'JIRA',
- caption: 'STREAM Board',
- duration: '4 min',
- subTasksCompleted: 25,
- recordsFinished: 1234,
- message: 'All 25 subtasks completed',
- status: TaskStatus.COMPLETE,
- },
- {
- id: 0,
- provider: 'jira',
- icon: ProviderIcons[Providers.JIRA](14, 14),
- title: 'JIRA',
- caption: 'LAKE Board',
- duration: '4 min',
- subTasksCompleted: 25,
- recordsFinished: 1234,
- message: 'All 25 subtasks completed',
- status: TaskStatus.COMPLETE,
- },
- ],
- stageHeaderClassName: 'complete',
- },
- {
- id: 2,
- name: 'stage-2',
- title: 'Stage 2',
- status: StageStatus.PENDING,
- icon: <Spinner size={14} intent={Intent.PRIMARY} />,
- tasks: [
- {
- id: 0,
- provider: 'jira',
- icon: ProviderIcons[Providers.JIRA](14, 14),
- title: 'JIRA',
- caption: 'EE Board',
- duration: '5 min',
- subTasksCompleted: 25,
- recordsFinished: 1234,
- message: 'Subtask 5/25: Extracting Issues',
- status: TaskStatus.ACTIVE,
- },
- {
- id: 0,
- provider: 'jira',
- icon: ProviderIcons[Providers.JIRA](14, 14),
- title: 'JIRA',
- caption: 'EE Bugs Board',
- duration: '0 min',
- subTasksCompleted: 0,
- recordsFinished: 0,
- message: 'Invalid Board ID',
- status: TaskStatus.FAILED,
- },
- ],
- stageHeaderClassName: 'active',
- },
- {
- id: 3,
- name: 'stage-3',
- title: 'Stage 3',
- status: StageStatus.PENDING,
- icon: null,
- tasks: [
- {
- id: 0,
- provider: 'github',
- icon: ProviderIcons[Providers.GITHUB](14, 14),
- title: 'GITHUB',
- caption: 'merico-dev/lake',
- duration: null,
- subTasksCompleted: 0,
- recordsFinished: 0,
- message: 'Subtasks pending',
- status: TaskStatus.CREATED,
- },
- ],
- stageHeaderClassName: 'pending',
- },
- {
- id: 4,
- name: 'stage-4',
- title: 'Stage 4',
- status: StageStatus.PENDING,
- icon: null,
- tasks: [
- {
- id: 0,
- providr: 'github',
- icon: ProviderIcons[Providers.GITHUB](14, 14),
- title: 'GITHUB',
- caption: 'merico-dev/lake',
- duration: null,
- subTasksCompleted: 0,
- recordsFinished: 0,
- message: 'Subtasks pending',
- status: TaskStatus.CREATED,
- },
- ],
- stageHeaderClassName: 'pending',
- },
-]
-
-const TEST_HISTORICAL_RUNS = [
- {
- id: 0,
- status: 'TASK_COMPLETED',
- statusLabel: 'Completed',
- statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:15 AM',
- duration: '15 min',
- },
- {
- id: 1,
- status: 'TASK_COMPLETED',
- statusLabel: 'Completed',
- statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:15 AM',
- duration: '15 min',
- },
- {
- id: 2,
- status: 'TASK_FAILED',
- statusLabel: 'Failed',
- statusIcon: <Icon icon='delete' size={14} color={Colors.RED5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:00 AM',
- duration: '0 min',
- },
- {
- id: 3,
- status: 'TASK_COMPLETED',
- statusLabel: 'Completed',
- statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:15 AM',
- duration: '15 min',
- },
- {
- id: 4,
- status: 'TASK_COMPLETED',
- statusLabel: 'Completed',
- statusIcon: <Icon icon='tick-circle' size={14} color={Colors.GREEN5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:15 AM',
- duration: '15 min',
- },
- {
- id: 5,
- status: 'TASK_FAILED',
- statusLabel: 'Failed',
- statusIcon: <Icon icon='delete' size={14} color={Colors.RED5} />,
- startedAt: '05/25/2022 0:00 AM',
- completedAt: '05/25/2022 0:00 AM',
- duration: '0 min',
- },
-]
const BlueprintDetail = (props) => {
// eslint-disable-next-line no-unused-vars
@@ -454,8 +61,7 @@ const BlueprintDetail = (props) => {
const { bId } = useParams()
const [blueprintId, setBlueprintId] = useState()
- // @todo: replace with live $blueprint from Hook
- const [activeBlueprint, setActiveBlueprint] = useState(TEST_RUN)
+ const [activeBlueprint, setActiveBlueprint] = useState(NullBlueprint)
// eslint-disable-next-line no-unused-vars
const [blueprintConnections, setBlueprintConnections] = useState([])
const [blueprintPipelines, setBlueprintPipelines] = useState([])
@@ -465,13 +71,14 @@ const BlueprintDetail = (props) => {
const [showCurrentRunTasks, setShowCurrentRunTasks] = useState(true)
const [showInspector, setShowInspector] = useState(false)
const [currentStages, setCurrentStages] = useState([])
- const [historicalRuns, setHistoricalRuns] = useState(TEST_HISTORICAL_RUNS)
+ const [historicalRuns, setHistoricalRuns] = useState([])
const pollTimer = 5000
const pollInterval = useRef()
const [autoRefresh, setAutoRefresh] = useState(false)
const [expandRun, setExpandRun] = useState(null)
+ const [isDownloading, setIsDownloading] = useState(false)
const {
// eslint-disable-next-line no-unused-vars
@@ -523,6 +130,8 @@ const BlueprintDetail = (props) => {
allowedProviders,
// eslint-disable-next-line no-unused-vars
detectPipelineProviders,
+ logfile: pipelineLogFilename,
+ getPipelineLogfile
} = usePipelineManager()
const buildPipelineStages = useCallback((tasks = []) => {
@@ -597,12 +206,40 @@ const BlueprintDetail = (props) => {
icon = <Icon icon='delete' size={14} color={Colors.RED5} />
break
case TaskStatus.CANCELLED:
+ icon = <Icon icon='undo' size={14} color={Colors.RED5} />
+ break
case TaskStatus.CREATED:
+ icon = <Icon icon='stopwatch' size={14} color={Colors.GRAY3} />
break
}
return icon
}
+ const downloadPipelineLog = useCallback((pipeline) => {
+ console.log(`>>> DOWNLOADING PIPELINE #${pipeline?.id} LOG...`, getPipelineLogfile(pipeline?.id))
+ setIsDownloading(true)
+ ToastNotification.clear()
+ let downloadStatus = 404
+ const checkDownloadStatus = async (pipeline) => {
+ const d = await request.get(getPipelineLogfile(pipeline?.id))
+ downloadStatus = d?.status
+ }
+ checkDownloadStatus()
+ if (pipeline?.id && downloadStatus === 200) {
+ saveAs(
+ getPipelineLogfile(pipeline?.id),
+ pipelineLogFilename
+ )
+ setIsDownloading(false)
+ } else if (pipeline?.id && downloadStatus === 404) {
+ ToastNotification.show({ message: 'Logfile not available', intent: 'danger', icon: 'error' })
+ setIsDownloading(false)
+ } else {
+ ToastNotification.show({ message: 'Pipeline Invalid or Missing', intent: 'danger', icon: 'error' })
+ setIsDownloading(false)
+ }
+ }, [getPipelineLogfile, pipelineLogFilename])
+
useEffect(() => {
setBlueprintId(bId)
console.log('>>> REQUESTED BLUEPRINT ID ===', bId)
@@ -610,7 +247,6 @@ const BlueprintDetail = (props) => {
useEffect(() => {
if (blueprintId) {
- // @todo: enable blueprint data fetch
fetchBlueprint(blueprintId)
fetchAllPipelines()
}
@@ -648,7 +284,6 @@ const BlueprintDetail = (props) => {
useEffect(() => {
console.log('>>>> FETCHED ALL PIPELINES..', pipelines, activeBlueprint?.id)
- // {id: 5, status: 'TASK_FAILED', statusLabel: 'Failed', statusIcon: <Icon icon='delete' size={14} color={Colors.RED5} />,startedAt: '05/25/2022 0:00 AM', completedAt: '05/25/2022 0:00 AM', duration: '0 min' },
setBlueprintPipelines(
pipelines.filter((p) => p.blueprintId === activeBlueprint?.id)
)
@@ -657,7 +292,6 @@ const BlueprintDetail = (props) => {
useEffect(() => {
console.log('>>>> RELATED BLUEPRINT PIPELINES..', blueprintPipelines)
setLastPipeline(blueprintPipelines[0])
- // blueprintPipelines.filter(p => p.status !== TaskStatus.RUNNING).map
setHistoricalRuns(
blueprintPipelines.map((p, pIdx) => ({
id: p.id,
@@ -694,7 +328,6 @@ const BlueprintDetail = (props) => {
stage: `Stage ${lastPipeline.stage}`,
tasksFinished: Number(lastPipeline.finishedTasks),
tasksTotal: Number(lastPipeline.totalTasks),
- // totalTasks: Number(lastPipeline.totalTasks),
error: lastPipeline.message || null,
}))
}
@@ -834,32 +467,6 @@ const BlueprintDetail = (props) => {
</div>
</div>
- {/* <div className='blueprint-connections' style={{ width: '100%', alignSelf: 'flex-start' }}>
- <h3>Overview</h3>
- <Card elevation={Elevation.TWO} style={{ padding: '2px' }}>
- <table className='bp3-html-table bp3-html-table-bordered connections-overview-table' style={{ width: '100%' }}>
- <thead>
- <tr>
- <th style={{ minWidth: '200px' }}>Data Connection</th>
- <th style={{ width: '100%' }}>Data Scope</th>
- </tr>
- </thead>
- <tbody>
- {blueprintConnections?.map((c, cIdx) => (
- <tr key={`connection-row-key-${cIdx}`}>
- <td>
- {c.name}
- </td>
- <td>
- {c.dataScope}{' '}
- </td>
- </tr>
- ))}
- </tbody>
- </table>
- </Card>
- </div> */}
-
<div
className='blueprint-run'
style={{
@@ -1016,7 +623,6 @@ const BlueprintDetail = (props) => {
>
<div
className='pipeline-task-activity' style={{
- // padding: '20px',
flex: 1,
padding: Object.keys(currentStages).length === 1 ? '0' : 0,
overflow: 'hidden',
@@ -1033,96 +639,7 @@ const BlueprintDetail = (props) => {
</div>
)}
</div>
- {/* {currentStages.map((stage, stageIdx) => (
- <div
- className='run-stage'
- key={`run-stage-key-${stageIdx}`}
- style={{ flex: 1, margin: '0 4px' }}
- >
- <h3
- className={`stage-header ${stage?.stageHeaderClassName}`}
- style={{ margin: '0', padding: '7px' }}
- >
- <span style={{ float: 'right' }}>{stage?.icon}</span>
- {stage?.title}
- </h3>
- {showCurrentRunTasks && (
- <div className='task-activity'>
- {stage.tasks.map((stageTask, stIdx) => (
- <div
- className='stage-task'
- key={`stage-task-key-${stIdx}`}
- style={{
- display: 'flex',
- flexDirection: 'column',
- }}
- >
- <div
- className='stage-task-info'
- style={{ display: 'flex', padding: '8px' }}
- >
- <div
- className='task-icon'
- style={{ minWidth: '24px' }}
- >
- {stageTask.icon}
- </div>
- <div
- className='task-title'
- style={{ flex: 1 }}
- >
- <div style={{ marginBottom: '8px' }}>
- <strong>{stageTask.title}</strong>{' '}
- {stageTask?.caption}
- </div>
- <div
- className='stage-task-progress'
- style={{
- color:
- stageTask?.status ===
- TaskStatus.FAILED
- ? StatusColors.FAILED
- : 'inherit',
- }}
- >
- <div>{stageTask?.message}</div>
- <div>
- {stageTask?.recordsFinished} records
- finished
- </div>
- </div>
- </div>
- <div
- className='task-duration'
- style={{
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- color: StatusColors[stageTask?.status],
- }}
- >
- {stageTask.duration}{' '}
- {stageTask?.status ===
- TaskStatus.FAILED && (
- <>
- ({TaskStatusLabels[TaskStatus.FAILED]})
- </>
- )}
- {stageTask?.status ===
- TaskStatus.ACTIVE && (
- <>
- ({TaskStatusLabels[TaskStatus.ACTIVE]})
- </>
- )}
- </div>
- </div>
- <Divider />
- </div>
- ))}
- </div>
- )}
- </div>
- ))} */}
+
<Button
icon={
showCurrentRunTasks ? 'chevron-down' : 'chevron-right'
@@ -1242,20 +759,20 @@ const BlueprintDetail = (props) => {
onClick={() => inspectRun(blueprintPipelines.find(p => p.id === run.id))}
/>
</Tooltip>
- {/* <Tooltip
+ <Tooltip
intent={Intent.PRIMARY}
content='View Full Log'
- > */}
- <Button
- intent={Intent.NONE}
- minimal
- small
- icon='document'
- style={{ marginLeft: '10px' }}
- // @todo: enable log view dialog support feature
- disabled
- />
- {/* </Tooltip> */}
+ >
+ <Button
+ intent={Intent.NONE}
+ loading={isDownloading}
+ minimal
+ small
+ icon='document'
+ style={{ marginLeft: '10px' }}
+ onClick={() => downloadPipelineLog(blueprintPipelines.find(p => p.id === run.id))}
+ />
+ </Tooltip>
<Tooltip
intent={Intent.PRIMARY}
content='Show Run Activity'
diff --git a/config-ui/src/pages/blueprints/create-blueprint.jsx b/config-ui/src/pages/blueprints/create-blueprint.jsx
index a8168e6e..df0aad25 100644
--- a/config-ui/src/pages/blueprints/create-blueprint.jsx
+++ b/config-ui/src/pages/blueprints/create-blueprint.jsx
@@ -1190,6 +1190,8 @@ const CreateBlueprint = (props) => {
onSave={handleTransformationSave}
onCancel={handleTransformationCancel}
onClear={handleTransformationClear}
+ fieldHasError={fieldHasError}
+ getFieldError={getFieldError}
jiraProxyError={jiraProxyError}
isFetchingJIRA={isFetchingJIRA}
/>
diff --git a/config-ui/src/pages/blueprints/index.jsx b/config-ui/src/pages/blueprints/index.jsx
index bd40cdb8..5a669db2 100644
--- a/config-ui/src/pages/blueprints/index.jsx
+++ b/config-ui/src/pages/blueprints/index.jsx
@@ -249,6 +249,9 @@ const Blueprints = (props) => {
case 'monthly':
setFilteredBlueprints(blueprints.filter(b => b.cronConfig === getCronPreset(activeFilterStatus).cronConfig))
break
+ case 'manual':
+ setFilteredBlueprints(blueprints.filter(b => b.isManual))
+ break
case 'custom':
setFilteredBlueprints(blueprints.filter(
b => b.cronConfig !== getCronPreset('hourly').cronConfig &&
diff --git a/config-ui/src/pages/configure/settings/github.jsx b/config-ui/src/pages/configure/settings/github.jsx
index b60bf47a..40686121 100644
--- a/config-ui/src/pages/configure/settings/github.jsx
+++ b/config-ui/src/pages/configure/settings/github.jsx
@@ -306,7 +306,7 @@ export default function GithubSettings (props) {
</>
)}
- {(entities?.length === 0 || entities.some(e => e.value === DataEntityTypes.CROSSDOMAIN)) && (
+ {(entities?.length === 0 || entities.every(e => e.value === DataEntityTypes.CROSSDOMAIN)) && (
<div className='headlineContainer'>
<h5>No Data Entities</h5>
<p className='description'>