diff --git a/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx b/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx index 22237cf1ef8..cefd7855ce7 100644 --- a/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx +++ b/config-ui/src/components/blueprints/ProviderTransformationSettings.jsx @@ -25,6 +25,7 @@ import GithubSettings from '@/pages/configure/settings/github' import AzureSettings from '@/pages/configure/settings/azure' import BitbucketSettings from '@/pages/configure/settings/bitbucket' import GiteeSettings from '@/pages/configure/settings/gitee' +import ZentaoSettings from '@/pages/configure/settings/zentao' // Transformation Higher-Order Component (HOC) Settings Loader const withTransformationSettings = ( @@ -70,6 +71,7 @@ const ProviderTransformationSettings = (props) => { [Providers.JIRA]: JiraSettings, [Providers.JENKINS]: JenkinsSettings, [Providers.TAPD]: TapdSettings, + [Providers.ZENTAO]: ZentaoSettings, [Providers.AZURE]: AzureSettings, [Providers.BITBUCKET]: BitbucketSettings, [Providers.GITEE]: GiteeSettings diff --git a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx index be956819fa8..f00eae0d69f 100644 --- a/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx +++ b/config-ui/src/components/blueprints/create-workflow/DataTransformations.jsx @@ -77,6 +77,7 @@ const DataTransformations = (props) => { const noTransformationsAvailable = useMemo( () => [Providers.TAPD].includes(configuredConnection?.provider) || + [Providers.ZENTAO].includes(configuredConnection?.provider) || ([Providers.GITLAB].includes(configuredConnection?.provider) && dataDomainsGroup[configuredConnection?.id].every( (e) => e.value !== DataDomainTypes.DEVOPS @@ -86,6 +87,7 @@ const DataTransformations = (props) => { configuredConnection?.id, dataDomainsGroup, Providers.TAPD, + Providers.ZENTAO, Providers.GITLAB ] ) diff --git a/config-ui/src/components/menus/PipelineConfigsMenu.jsx b/config-ui/src/components/menus/PipelineConfigsMenu.jsx index d45a4312ec2..930912b5424 100644 --- a/config-ui/src/components/menus/PipelineConfigsMenu.jsx +++ b/config-ui/src/components/menus/PipelineConfigsMenu.jsx @@ -23,6 +23,7 @@ import { gitextractorConfig as sampleGitextractorPipelineConfig } from '@/data/p import { githubConfig as sampleGithubPipelineConfig } from '@/data/pipeline-config-samples/github' import { gitlabConfig as sampleGitlabPipelineConfig } from '@/data/pipeline-config-samples/gitlab' import { jiraConfig as sampleJiraPipelineConfig } from '@/data/pipeline-config-samples/jira' +import { zentaoConfig as sampleZentaoPipelineConfig } from '@/data/pipeline-config-samples/zentao' import { jenkinsConfig as sampleJenkinsPipelineConfig } from '@/data/pipeline-config-samples/jenkins' import { feishuConfig as sampleFeishuPipelineConfig } from '@/data/pipeline-config-samples/feishu' import { dbtConfig as sampleDbtPipelineConfig } from '@/data/pipeline-config-samples/dbt' @@ -137,6 +138,15 @@ const PipelineConfigsMenu = (props) => { ) } /> + + setRawConfiguration( + JSON.stringify(sampleZentaoPipelineConfig, null, ' ') + ) + } + /> ) } diff --git a/config-ui/src/components/pipelines/StageTaskName.jsx b/config-ui/src/components/pipelines/StageTaskName.jsx index 763175ef2a0..cd640005fc5 100644 --- a/config-ui/src/components/pipelines/StageTaskName.jsx +++ b/config-ui/src/components/pipelines/StageTaskName.jsx @@ -122,6 +122,7 @@ const StageTaskName = (props) => { <>{ProviderLabels.JENKINS} )} {task.plugin === Providers.TAPD && <>{ProviderLabels.TAPD}} + {task.plugin === Providers.ZENTAO && <>{ProviderLabels.ZENTAO}} {task.plugin === Providers.JIRA && ( <>Board ID {task.options.boardId} )} diff --git a/config-ui/src/data/Providers.js b/config-ui/src/data/Providers.js index 1a4d4f9795f..eb4c6313439 100644 --- a/config-ui/src/data/Providers.js +++ b/config-ui/src/data/Providers.js @@ -22,6 +22,7 @@ import { ReactComponent as JenkinsProviderIcon } from '@/images/integrations/jen 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 { ReactComponent as ZentaoProviderIcon } from '@/images/integrations/zentao.svg' import { ReactComponent as AzureProviderIcon } from '@/images/integrations/azure.svg' import { ReactComponent as BitbucketProviderIcon } from '@/images/integrations/bitbucket.svg' import { ReactComponent as GiteeProviderIcon } from '@/images/integrations/gitee.svg' @@ -50,6 +51,7 @@ const Providers = { DBT: 'dbt', STARROCKS: 'starrocks', TAPD: 'tapd', + ZENTAO: 'zentao', AZURE: 'azure', BITBUCKET: 'bitbucket', GITEE: 'gitee', @@ -76,7 +78,8 @@ const ProviderLabels = { AE: 'Analysis Engine (AE)', DBT: 'Data Build Tool (DBT)', STARROCKS: 'StarRocks', - TAPD: 'TAPD', + TAPD: 'Tapd', + ZENTAO: 'Zentao', AZURE: 'Azure CI', BITBUCKET: 'BitBucket', GITEE: 'Gitee', @@ -146,6 +149,19 @@ const ProviderFormLabels = { ) }, + zentao: { + name: 'Connection Name', + endpoint: 'Endpoint URL', + proxy: 'Proxy URL', + username: 'Username', + password: 'Password', + rateLimitPerHour: ( + <> + Rate Limit (per hour) + + + ) + }, jira: { name: 'Connection Name', endpoint: 'Endpoint URL', @@ -308,6 +324,14 @@ const ProviderFormPlaceholders = { password: 'eg. ************', rateLimitPerHour: '1000' }, + zentao: { + name: 'eg. Zentao', + endpoint: 'URL eg. http://subdomain.domain:port/api.php/v1/', + proxy: 'eg. http://proxy.localhost:8080', + username: 'eg. devlake', + password: 'eg. ************', + rateLimitPerHour: '1000' + }, jira: { name: 'eg. JIRA', endpoint: 'eg. https://your-domain.atlassian.net/rest/', @@ -366,6 +390,9 @@ const ProviderIcons = { [Providers.TAPD]: (w, h) => ( ), + [Providers.ZENTAO]: (w, h) => ( + + ), [Providers.JIRA]: (w, h) => ( ), diff --git a/config-ui/src/data/availablePlugins.js b/config-ui/src/data/availablePlugins.js index 4433f79cb80..dc5f79d2d0b 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', 'tapd'] +const AVAILABLE_PLUGINS = ['gitlab', 'jira', 'jenkins', 'github', 'tapd', 'zentao'] module.exports = AVAILABLE_PLUGINS diff --git a/config-ui/src/data/integrations.jsx b/config-ui/src/data/integrations.jsx index 1a10f7aa0f9..b5f0165c3a2 100644 --- a/config-ui/src/data/integrations.jsx +++ b/config-ui/src/data/integrations.jsx @@ -30,6 +30,7 @@ import { ReactComponent as JenkinsProvider } from '@/images/integrations/jenkins 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 { ReactComponent as ZentaoProvider } from '@/images/integrations/zentao.svg' import { ReactComponent as AzureProvider } from '@/images/integrations/azure.svg' import { ReactComponent as BitbucketProvider } from '@/images/integrations/bitbucket.svg' import { ReactComponent as GiteeProvider } from '@/images/integrations/gitee.svg' @@ -102,6 +103,27 @@ const integrationsData = [ // relocated to ProviderTransformationSettings since v0.12.0 settings: {} }, + { + id: Providers.ZENTAO, + type: ProviderTypes.INTEGRATION, + enabled: true, + multiConnection: true, + isBeta: true, + name: ProviderLabels.ZENTAO, + icon: ( + + ), + iconDashboard: ( + + ), + // relocated to ProviderTransformationSettings since v0.12.0 + settings: {} + }, { id: Providers.JIRA, type: ProviderTypes.INTEGRATION, diff --git a/config-ui/src/data/pipeline-config-samples/zentao.js b/config-ui/src/data/pipeline-config-samples/zentao.js new file mode 100644 index 00000000000..22590ee722f --- /dev/null +++ b/config-ui/src/data/pipeline-config-samples/zentao.js @@ -0,0 +1,32 @@ +/* + * 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 zentaoConfig = [ + [ + { + plugin: 'zentao', + options: { + connectionId: 1, + productId: 1, + projectId: 1, + executionId: 1, + } + } + ] +] + +export { zentaoConfig } diff --git a/config-ui/src/hooks/useConnectionValidation.jsx b/config-ui/src/hooks/useConnectionValidation.jsx index fbf109a672c..5596c6634d6 100644 --- a/config-ui/src/hooks/useConnectionValidation.jsx +++ b/config-ui/src/hooks/useConnectionValidation.jsx @@ -96,6 +96,7 @@ function useConnectionValidation({ } break case Providers.JIRA: + case Providers.ZENTAO: case Providers.JENKINS: if (!username || username.length <= 2) { errs.push('Username is required') diff --git a/config-ui/src/hooks/useIntegrations.jsx b/config-ui/src/hooks/useIntegrations.jsx index cbf202499be..6b3e6645e82 100644 --- a/config-ui/src/hooks/useIntegrations.jsx +++ b/config-ui/src/hooks/useIntegrations.jsx @@ -27,6 +27,7 @@ import GitHubGraphqlPlugin from '@/registry/plugins/github_graphql.json' import GitLabPlugin from '@/registry/plugins/gitlab.json' import JenkinsPlugin from '@/registry/plugins/jenkins.json' import TapdPlugin from '@/registry/plugins/tapd.json' +import ZentaoPlugin from '@/registry/plugins/zentao.json' import AzurePlugin from '@/registry/plugins/azure.json' import BitbucketPlugin from '@/registry/plugins/bitbucket.json' import GiteePlugin from '@/registry/plugins/gitee.json' @@ -51,6 +52,7 @@ function useIntegrations( GitLabPlugin, JenkinsPlugin, TapdPlugin, + ZentaoPlugin, AzurePlugin, BitbucketPlugin, GiteePlugin, diff --git a/config-ui/src/images/integrations/zentao.svg b/config-ui/src/images/integrations/zentao.svg new file mode 100644 index 00000000000..dc195d06768 --- /dev/null +++ b/config-ui/src/images/integrations/zentao.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/config-ui/src/pages/blueprints/blueprint-settings.jsx b/config-ui/src/pages/blueprints/blueprint-settings.jsx index 75ebd2ef0a4..da4ee703c47 100644 --- a/config-ui/src/pages/blueprints/blueprint-settings.jsx +++ b/config-ui/src/pages/blueprints/blueprint-settings.jsx @@ -512,6 +512,7 @@ const BlueprintSettings = (props) => { dataDomainsGroup[configuredConnection?.id]?.length > 0 break case Providers.TAPD: + case Providers.ZENTAO: isValid = dataDomainsGroup[configuredConnection?.id]?.length > 0 break default: diff --git a/config-ui/src/pages/configure/connections/ConnectionForm.jsx b/config-ui/src/pages/configure/connections/ConnectionForm.jsx index 23b2b6ebcfb..b8120f24166 100644 --- a/config-ui/src/pages/configure/connections/ConnectionForm.jsx +++ b/config-ui/src/pages/configure/connections/ConnectionForm.jsx @@ -805,6 +805,7 @@ export default function ConnectionForm(props) { Providers.JIRA, Providers.JENKINS, Providers.TAPD, + Providers.ZENTAO, Providers.AZURE, Providers.BITBUCKET, Providers.GITEE diff --git a/config-ui/src/pages/configure/settings/zentao.jsx b/config-ui/src/pages/configure/settings/zentao.jsx new file mode 100644 index 00000000000..96de85a1f8e --- /dev/null +++ b/config-ui/src/pages/configure/settings/zentao.jsx @@ -0,0 +1,57 @@ +/* + * 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, { useEffect, useState } from 'react' +import { useParams, useHistory } from 'react-router-dom' +import { DataDomainTypes } from '@/data/DataDomains' + +import '@/styles/integration.scss' +import '@/styles/connections.scss' + +export default function ZentaoSettings(props) { + const { + provider, + connection, + dataDomains = [], + onSettingsChange = () => {} + } = props + const history = useHistory() + + const cancel = () => { + history.push(`/integrations/${provider.id}`) + } + + return ( + <> +
+

No Additional Settings

+

+ This integration doesn’t require any configuration. You can continue + to  + + add other data connections + +   or trigger collection at the{' '} + + previous page + + . +

+
+ + ) +} diff --git a/config-ui/src/registry/plugins/zentao.json b/config-ui/src/registry/plugins/zentao.json new file mode 100644 index 00000000000..b89a893fb2d --- /dev/null +++ b/config-ui/src/registry/plugins/zentao.json @@ -0,0 +1,55 @@ +{ + "id": "zentao", + "name": "ZENTAO", + "type": "integration", + "enabled": true, + "multiConnection": true, + "connectionLimit": 0, + "isBeta": true, + "isProvider": true, + "icon": "src/images/integrations/zentao.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. Zentao", + "endpoint": "URL eg. http://subdomain.domain:port/api.php/v1/", + "proxy": "eg. http://proxy.localhost:8080", + "username": "eg. admin", + "password": "eg. ************", + "rateLimit": "1000" + }, + "tooltips": { + } + }, + "availableDataDomains": ["TICKET"], + "transformations": { + "scopes": { + "options": { + } + }, + "default": { + "issueTypeRequirement": "", + "issueTypeBug": "", + "issueTypeIncident": "", + "productionPattern": "", + "deploymentPattern": "" + } + } +} \ No newline at end of file diff --git a/plugins/dora/e2e/calculate_change_lead_time_test.go b/plugins/dora/e2e/calculate_change_lead_time_test.go index 9667a5040e3..81c18107fb4 100644 --- a/plugins/dora/e2e/calculate_change_lead_time_test.go +++ b/plugins/dora/e2e/calculate_change_lead_time_test.go @@ -44,7 +44,7 @@ func TestCalculateCLTimeDataFlow(t *testing.T) { dataflowTester.FlushTabler(&code.PullRequest{}) // import raw data table - dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks_changeleadtime.csv", &devops.CICDTask{}) + dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks.csv", &devops.CICDTask{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_requests.csv", &code.PullRequest{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/commits_diffs.csv", &code.CommitsDiff{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_pipeline_commits.csv", &devops.CiCDPipelineCommit{}) diff --git a/plugins/dora/e2e/connect_incident_to_deploy_test.go b/plugins/dora/e2e/connect_incident_to_deploy_test.go index 4e508f5e8b8..0ed940a9516 100644 --- a/plugins/dora/e2e/connect_incident_to_deploy_test.go +++ b/plugins/dora/e2e/connect_incident_to_deploy_test.go @@ -42,7 +42,7 @@ func TestConnectIncidentToDeploymentDataFlow(t *testing.T) { }, } // import raw data table - dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks_changeleadtime.csv", &devops.CICDTask{}) + dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks.csv", &devops.CICDTask{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/project_mapping.csv", &crossdomain.ProjectMapping{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/board_issues.csv", &ticket.BoardIssue{}) dataflowTester.ImportCsvIntoTabler("./raw_tables/issues.csv", &ticket.Issue{}) diff --git a/plugins/dora/e2e/env_enricher_test.go b/plugins/dora/e2e/env_enricher_test.go index d2aa98c9008..520156a4ac5 100644 --- a/plugins/dora/e2e/env_enricher_test.go +++ b/plugins/dora/e2e/env_enricher_test.go @@ -18,6 +18,7 @@ limitations under the License. package e2e import ( + "github.com/apache/incubator-devlake/models/domainlayer/crossdomain" "testing" "github.com/apache/incubator-devlake/helpers/e2ehelper" @@ -33,7 +34,7 @@ func TestEnrichEnvDataFlow(t *testing.T) { taskData := &tasks.DoraTaskData{ Options: &tasks.DoraOptions{ - RepoId: "github:GithubRepo:1:384111310", + ProjectName: "project1", TransformationRules: tasks.TransformationRules{ ProductionPattern: "(?i)deploy", StagingPattern: "(?i)stag", @@ -42,35 +43,14 @@ func TestEnrichEnvDataFlow(t *testing.T) { }, } - dataflowTester.FlushTabler(&devops.CICDTask{}) - // import raw data table - dataflowTester.ImportCsvIntoTabler("./raw_tables/lake_cicd_pipeline_commits.csv", &devops.CiCDPipelineCommit{}) - dataflowTester.ImportCsvIntoTabler("./raw_tables/lake_cicd_tasks.csv", &devops.CICDTask{}) + dataflowTester.ImportCsvIntoTabler("./raw_tables/project_mapping.csv", &crossdomain.ProjectMapping{}) + dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks_for_enrich_env.csv", &devops.CICDTask{}) // verify enrich with repoId dataflowTester.Subtask(tasks.EnrichTaskEnvMeta, taskData) dataflowTester.VerifyTableWithOptions(&devops.CICDTask{}, e2ehelper.TableOptions{ - CSVRelPath: "./snapshot_tables/lake_cicd_tasks.csv", - IgnoreTypes: []interface{}{common.NoPKModel{}}, - }) - - // verify enrich with prefix - dataflowTester.FlushTabler(&devops.CICDTask{}) - dataflowTester.ImportCsvIntoTabler("./raw_tables/lake_cicd_tasks_for_prefix.csv", &devops.CICDTask{}) - taskDataPrefix := &tasks.DoraTaskData{ - Options: &tasks.DoraOptions{ - TransformationRules: tasks.TransformationRules{ - ProductionPattern: "(?i)deploy", - StagingPattern: "(?i)stag", - TestingPattern: "(?i)test", - }, - Prefix: "jenkins", - }, - } - dataflowTester.Subtask(tasks.EnrichTaskEnvMeta, taskDataPrefix) - dataflowTester.VerifyTableWithOptions(&devops.CICDTask{}, e2ehelper.TableOptions{ - CSVRelPath: "./snapshot_tables/lake_cicd_tasks_prefix.csv", + CSVRelPath: "./snapshot_tables/enrich_cicd_tasks.csv", IgnoreTypes: []interface{}{common.NoPKModel{}}, }) } diff --git a/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv b/plugins/dora/e2e/raw_tables/cicd_tasks.csv similarity index 100% rename from plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv rename to plugins/dora/e2e/raw_tables/cicd_tasks.csv diff --git a/plugins/dora/e2e/raw_tables/cicd_tasks_for_enrich_env.csv b/plugins/dora/e2e/raw_tables/cicd_tasks_for_enrich_env.csv new file mode 100644 index 00000000000..44c34724df1 --- /dev/null +++ b/plugins/dora/e2e/raw_tables/cicd_tasks_for_enrich_env.csv @@ -0,0 +1,19 @@ +id,name,pipeline_id,status,result,type,environment,duration_sec,started_date,finished_date,cicd_scope_id +task10,deployxIG,pipeline110,DONE,SUCCESS,DEPLOYMENT,,,2022-07-19 22:06:28,2022-11-13 22:37:21,cicd1 +task11,deploya,pipeline111,DONE,SUCCESS,DEPLOYMENT,,,2022-08-06 14:06:50,2022-11-13 00:07:21,cicd1 +task12,deployc,pipeline112,DONE,SUCCESS,DEPLOYMENT,,,2022-08-23 17:44:05,2022-11-02 07:21:09,cicd2 +task13,deploy,pipeline113,DONE,SUCCESS,DEPLOYMENT,,,2022-08-30 23:45:29,2022-11-28 00:46:47,cicd1 +task14,deployp0;,pipeline114,DONE,SUCCESS,DEPLOYMENT,,,2022-09-07 02:49:26,2022-11-16 20:34:01,cicd1 +task15,deployY{,pipeline115,DONE,SUCCESS,DEPLOYMENT,,,2022-09-27 01:07:50,2022-11-19 07:17:33,cicd2 +task16,deploy8',pipeline116,DONE,SUCCESS,DEPLOYMENT,,,2022-09-30 21:05:38,2022-11-08 07:56:03,cicd1 +task17,deployKd%,pipeline117,IN_PROGRESS,,DEPLOYMENT,,,2022-10-09 06:42:02,,cicd1 +task19,deploy1,pipeline119,DONE,FAILURE,DEPLOYMENT,,,2022-10-24 18:41:04,2022-11-24 04:26:48,cicd1 +task21,deploy^^.,pipeline39,DONE,FAILURE,DEPLOYMENT,,,2004-01-10 03:31:11,2022-11-28 20:41:59,cicd1 +task22,deploy,pipeline35,DONE,SUCCESS,DEPLOYMENT,,,2000-10-25 09:57:28,2022-11-28 21:24:02,cicd1 +task23,deploy,pipeline36,IN_PROGRESS,,DEPLOYMENT,,,2005-02-07 11:03:27,2022-11-05 18:18:03,cicd1 +task24,deploym,pipeline12,IN_PROGRESS,,DEPLOYMENT,,,2015-08-11 19:58:06,2022-11-01 22:31:56,cicd1 +task25,deploy$p<,pipeline26,DONE,FAILURE,,,,2014-02-06 13:42:43,2022-11-30 08:01:38,cicd3 +task26,deployb>@,pipeline20,IN_PROGRESS,,DEPLOYMENT,,,2016-08-26 05:41:49,2022-11-15 07:31:46,cicd2 +task27,deployKfn,pipeline37,DONE,SUCCESS,DEPLOYMENT,,,2003-12-13 23:19:14,2022-11-11 18:29:31,cicd2 +task28,deployl?,pipeline29,IN_PROGRESS,,DEPLOYMENT,,,2007-01-19 01:13:39,2022-11-24 05:39:46,cicd3 +task29,deployUb,pipeline27,IN_PROGRESS,,DEPLOYMENT,,,2006-05-20 18:17:13,2022-11-28 10:13:51,cicd2 diff --git a/plugins/dora/e2e/raw_tables/lake_cicd_tasks.csv b/plugins/dora/e2e/raw_tables/lake_cicd_tasks.csv deleted file mode 100644 index 5d0b739920c..00000000000 --- a/plugins/dora/e2e/raw_tables/lake_cicd_tasks.csv +++ /dev/null @@ -1,25 +0,0 @@ -id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,pipeline_id,result,status,type,duration_sec,started_date,finished_date,environment -github:GithubJob:1:8269045794,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,2,"",deploy,github:GithubRun:1:3022052551,FAILURE,DONE,"DEPLOYMENT",72,2022-09-09 11:27:13,2022-09-09 11:28:25,"" -github:GithubJob:1:8269045795,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,3,"",test (ubuntu-latest),github:GithubRun:1:3022052557,SUCCESS,DONE,"DEPLOYMENT",191,2022-09-09 11:27:42,2022-09-09 11:30:53,"" -github:GithubJob:1:8269045819,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,1,"",commit-msg,github:GithubRun:1:3022052555,SUCCESS,DONE,"DEPLOYMENT",8,2022-09-09 11:26:38,2022-09-09 11:26:46,"" -github:GithubJob:1:8269045928,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,4,"",e2e-mysql,github:GithubRun:1:3022052558,SUCCESS,DONE,"",200,2022-09-09 11:28:00,2022-09-09 11:31:20,"" -github:GithubJob:1:8269046116,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,6,"",check Apache license header,github:GithubRun:1:3022052589,SUCCESS,DONE,"",57,2022-09-09 11:28:34,2022-09-09 11:29:31,"" -github:GithubJob:1:8269415129,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,5,"",deploy,github:GithubRun:1:3022190519,FAILURE,DONE,"",84,2022-09-09 11:51:21,2022-09-09 11:52:45,"" -github:GithubJob:1:8269415130,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,9,"",test (ubuntu-latest),github:GithubRun:1:3022190524,SUCCESS,DONE,"",182,2022-09-09 11:50:31,2022-09-09 11:53:33,"" -github:GithubJob:1:8269415142,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,10,"",check Apache license header,github:GithubRun:1:3022190525,SUCCESS,DONE,"",63,2022-09-09 11:52:06,2022-09-09 11:53:09,"" -github:GithubJob:1:8269415165,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,8,"",commit-msg,github:GithubRun:1:3022190521,SUCCESS,DONE,"",10,2022-09-09 11:52:46,2022-09-09 11:52:56,"" -github:GithubJob:1:8269415315,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,7,"",deployment,github:GithubRun:1:3022190520,SUCCESS,DONE,"",242,2022-09-09 11:53:45,2022-09-09 11:57:47,"" -github:GithubJob:1:8269598312,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,14,"",e2e-mysql,github:GithubRun:1:3022255519,SUCCESS,DONE,"",828,2022-09-09 12:05:04,2022-09-09 12:18:52,"" -github:GithubJob:1:8269598316,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,11,"",test (ubuntu-latest),github:GithubRun:1:3022255511,SUCCESS,DONE,"",1016,2022-09-09 12:05:49,2022-09-09 12:22:45,"" -github:GithubJob:1:8269598318,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,12,"",lint,github:GithubRun:1:3022255516,FAILURE,DONE,"",62,2022-09-09 12:02:28,2022-09-09 12:03:30,"" -github:GithubJob:1:8269598325,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,13,"",check Apache license header,github:GithubRun:1:3022255517,SUCCESS,DONE,"",52,2022-09-09 12:03:51,2022-09-09 12:04:43,"" -github:GithubJob:1:8269598469,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,15,"",commit-msg,github:GithubRun:1:3022255524,FAILURE,DONE,"",41,2022-09-09 12:06:55,2022-09-09 12:07:36,"" -github:GithubJob:1:8269729324,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,16,"",e2e-mysql,github:GithubRun:1:3022300995,SUCCESS,DONE,"",1007,2022-09-09 12:10:08,2022-09-09 12:26:55,"" -github:GithubJob:1:8269729327,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,18,"",xx_deploy_xx,github:GithubRun:1:3022301001,FAILURE,DONE,"",82,2022-09-09 12:11:22,2022-09-09 12:12:44,"" -github:GithubJob:1:8269729550,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,19,"",build_deployment,github:GithubRun:1:3022301007,SUCCESS,DONE,"",6,2022-09-09 12:12:02,2022-09-09 12:12:08,"" -github:GithubJob:1:8269729553,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,17,"",build_deploy,github:GithubRun:1:3022300997,SUCCESS,DONE,"",57,2022-09-09 12:12:52,2022-09-09 12:13:49,"" -github:GithubJob:1:8269729821,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,20,"",deployment,github:GithubRun:1:3022301033,SUCCESS,DONE,"",991,2022-09-09 12:13:58,2022-09-09 12:30:29,"" -github:GithubJob:1:8269872730,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,21,"",deploy,github:GithubRun:1:3022348786,SUCCESS,DONE,"",1006,2022-09-09 12:21:30,2022-09-09 12:38:16,"" -github:GithubJob:1:7773010057,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,17,"",build_deploy,github:GithubRun:1:3022300997,SUCCESS,DONE,"",57,2022-09-09 12:12:52,2022-09-09 12:13:49,"" -github:GithubJob:1:7773010060,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,20,"",deployment,github:GithubRun:1:3022301033,SUCCESS,DONE,"",991,2022-09-09 12:13:58,2022-09-09 12:30:29,"" -github:GithubJob:1:7773010062,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,21,"",deploy,github:GithubRun:1:3022348786,SUCCESS,DONE,"",1006,2022-09-09 12:21:30,2022-09-09 12:38:16,"" \ No newline at end of file diff --git a/plugins/dora/e2e/raw_tables/lake_cicd_tasks_for_prefix.csv b/plugins/dora/e2e/raw_tables/lake_cicd_tasks_for_prefix.csv deleted file mode 100644 index bd241115757..00000000000 --- a/plugins/dora/e2e/raw_tables/lake_cicd_tasks_for_prefix.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,pipeline_id,result,status,type,duration_sec,started_date,finished_date,environment -jenkins:GithubJob:1:8269872730,2022-09-15 03:20:03.867,2022-09-15 03:20:03.867,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_jobs,21,"",deploy,jenkins:GithubRun:1:3022348786,SUCCESS,DONE,"",1006,2022-09-09 12:21:30,2022-09-09 12:38:16,"" diff --git a/plugins/dora/e2e/snapshot_tables/enrich_cicd_tasks.csv b/plugins/dora/e2e/snapshot_tables/enrich_cicd_tasks.csv new file mode 100644 index 00000000000..3c3d03fcbc4 --- /dev/null +++ b/plugins/dora/e2e/snapshot_tables/enrich_cicd_tasks.csv @@ -0,0 +1,19 @@ +id,name,pipeline_id,result,status,type,environment,duration_sec,started_date,finished_date,cicd_scope_id +task10,deployxIG,pipeline110,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-07-19T22:06:28.000+00:00,2022-11-13T22:37:21.000+00:00,cicd1 +task11,deploya,pipeline111,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-08-06T14:06:50.000+00:00,2022-11-13T00:07:21.000+00:00,cicd1 +task12,deployc,pipeline112,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-08-23T17:44:05.000+00:00,2022-11-02T07:21:09.000+00:00,cicd2 +task13,deploy,pipeline113,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-08-30T23:45:29.000+00:00,2022-11-28T00:46:47.000+00:00,cicd1 +task14,deployp0;,pipeline114,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-09-07T02:49:26.000+00:00,2022-11-16T20:34:01.000+00:00,cicd1 +task15,deployY{,pipeline115,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-09-27T01:07:50.000+00:00,2022-11-19T07:17:33.000+00:00,cicd2 +task16,deploy8',pipeline116,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2022-09-30T21:05:38.000+00:00,2022-11-08T07:56:03.000+00:00,cicd1 +task17,deployKd%,pipeline117,,IN_PROGRESS,DEPLOYMENT,PRODUCTION,0,2022-10-09T06:42:02.000+00:00,,cicd1 +task19,deploy1,pipeline119,FAILURE,DONE,DEPLOYMENT,PRODUCTION,0,2022-10-24T18:41:04.000+00:00,2022-11-24T04:26:48.000+00:00,cicd1 +task21,deploy^^.,pipeline39,FAILURE,DONE,DEPLOYMENT,PRODUCTION,0,2004-01-10T03:31:11.000+00:00,2022-11-28T20:41:59.000+00:00,cicd1 +task22,deploy,pipeline35,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2000-10-25T09:57:28.000+00:00,2022-11-28T21:24:02.000+00:00,cicd1 +task23,deploy,pipeline36,,IN_PROGRESS,DEPLOYMENT,PRODUCTION,0,2005-02-07T11:03:27.000+00:00,2022-11-05T18:18:03.000+00:00,cicd1 +task24,deploym,pipeline12,,IN_PROGRESS,DEPLOYMENT,PRODUCTION,0,2015-08-11T19:58:06.000+00:00,2022-11-01T22:31:56.000+00:00,cicd1 +task25,deploy$p<,pipeline26,FAILURE,DONE,,,,2014-02-06T13:42:43.000+00:00,2022-11-30T08:01:38.000+00:00,cicd3 +task26,deployb>@,pipeline20,,IN_PROGRESS,DEPLOYMENT,PRODUCTION,0,2016-08-26T05:41:49.000+00:00,2022-11-15T07:31:46.000+00:00,cicd2 +task27,deployKfn,pipeline37,SUCCESS,DONE,DEPLOYMENT,PRODUCTION,0,2003-12-13T23:19:14.000+00:00,2022-11-11T18:29:31.000+00:00,cicd2 +task28,deployl?,pipeline29,,IN_PROGRESS,DEPLOYMENT,,,2007-01-19T01:13:39.000+00:00,2022-11-24T05:39:46.000+00:00,cicd3 +task29,deployUb,pipeline27,,IN_PROGRESS,DEPLOYMENT,PRODUCTION,0,2006-05-20T18:17:13.000+00:00,2022-11-28T10:13:51.000+00:00,cicd2 diff --git a/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks.csv b/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks.csv deleted file mode 100644 index f444b0483f2..00000000000 --- a/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks.csv +++ /dev/null @@ -1,25 +0,0 @@ -id,name,pipeline_id,result,status,type,environment,duration_sec,started_date,finished_date,cicd_scope_id -github:GithubJob:1:7773010057,build_deploy,github:GithubRun:1:3022300997,SUCCESS,DONE,,PRODUCTION,57,2022-09-09T12:12:52.000+00:00,2022-09-09T12:13:49.000+00:00, -github:GithubJob:1:7773010060,deployment,github:GithubRun:1:3022301033,SUCCESS,DONE,,PRODUCTION,991,2022-09-09T12:13:58.000+00:00,2022-09-09T12:30:29.000+00:00, -github:GithubJob:1:7773010062,deploy,github:GithubRun:1:3022348786,SUCCESS,DONE,,PRODUCTION,1006,2022-09-09T12:21:30.000+00:00,2022-09-09T12:38:16.000+00:00, -github:GithubJob:1:8269045794,deploy,github:GithubRun:1:3022052551,FAILURE,DONE,DEPLOYMENT,PRODUCTION,72,2022-09-09T11:27:13.000+00:00,2022-09-09T11:28:25.000+00:00, -github:GithubJob:1:8269045795,test (ubuntu-latest),github:GithubRun:1:3022052557,SUCCESS,DONE,DEPLOYMENT,,191,2022-09-09T11:27:42.000+00:00,2022-09-09T11:30:53.000+00:00, -github:GithubJob:1:8269045819,commit-msg,github:GithubRun:1:3022052555,SUCCESS,DONE,DEPLOYMENT,,8,2022-09-09T11:26:38.000+00:00,2022-09-09T11:26:46.000+00:00, -github:GithubJob:1:8269045928,e2e-mysql,github:GithubRun:1:3022052558,SUCCESS,DONE,,,200,2022-09-09T11:28:00.000+00:00,2022-09-09T11:31:20.000+00:00, -github:GithubJob:1:8269046116,check Apache license header,github:GithubRun:1:3022052589,SUCCESS,DONE,,,57,2022-09-09T11:28:34.000+00:00,2022-09-09T11:29:31.000+00:00, -github:GithubJob:1:8269415129,deploy,github:GithubRun:1:3022190519,FAILURE,DONE,,PRODUCTION,84,2022-09-09T11:51:21.000+00:00,2022-09-09T11:52:45.000+00:00, -github:GithubJob:1:8269415130,test (ubuntu-latest),github:GithubRun:1:3022190524,SUCCESS,DONE,,,182,2022-09-09T11:50:31.000+00:00,2022-09-09T11:53:33.000+00:00, -github:GithubJob:1:8269415142,check Apache license header,github:GithubRun:1:3022190525,SUCCESS,DONE,,,63,2022-09-09T11:52:06.000+00:00,2022-09-09T11:53:09.000+00:00, -github:GithubJob:1:8269415165,commit-msg,github:GithubRun:1:3022190521,SUCCESS,DONE,,,10,2022-09-09T11:52:46.000+00:00,2022-09-09T11:52:56.000+00:00, -github:GithubJob:1:8269415315,deployment,github:GithubRun:1:3022190520,SUCCESS,DONE,,PRODUCTION,242,2022-09-09T11:53:45.000+00:00,2022-09-09T11:57:47.000+00:00, -github:GithubJob:1:8269598312,e2e-mysql,github:GithubRun:1:3022255519,SUCCESS,DONE,,,828,2022-09-09T12:05:04.000+00:00,2022-09-09T12:18:52.000+00:00, -github:GithubJob:1:8269598316,test (ubuntu-latest),github:GithubRun:1:3022255511,SUCCESS,DONE,,,1016,2022-09-09T12:05:49.000+00:00,2022-09-09T12:22:45.000+00:00, -github:GithubJob:1:8269598318,lint,github:GithubRun:1:3022255516,FAILURE,DONE,,,62,2022-09-09T12:02:28.000+00:00,2022-09-09T12:03:30.000+00:00, -github:GithubJob:1:8269598325,check Apache license header,github:GithubRun:1:3022255517,SUCCESS,DONE,,,52,2022-09-09T12:03:51.000+00:00,2022-09-09T12:04:43.000+00:00, -github:GithubJob:1:8269598469,commit-msg,github:GithubRun:1:3022255524,FAILURE,DONE,,,41,2022-09-09T12:06:55.000+00:00,2022-09-09T12:07:36.000+00:00, -github:GithubJob:1:8269729324,e2e-mysql,github:GithubRun:1:3022300995,SUCCESS,DONE,,,1007,2022-09-09T12:10:08.000+00:00,2022-09-09T12:26:55.000+00:00, -github:GithubJob:1:8269729327,xx_deploy_xx,github:GithubRun:1:3022301001,FAILURE,DONE,,PRODUCTION,82,2022-09-09T12:11:22.000+00:00,2022-09-09T12:12:44.000+00:00, -github:GithubJob:1:8269729550,build_deployment,github:GithubRun:1:3022301007,SUCCESS,DONE,,PRODUCTION,6,2022-09-09T12:12:02.000+00:00,2022-09-09T12:12:08.000+00:00, -github:GithubJob:1:8269729553,build_deploy,github:GithubRun:1:3022300997,SUCCESS,DONE,,PRODUCTION,57,2022-09-09T12:12:52.000+00:00,2022-09-09T12:13:49.000+00:00, -github:GithubJob:1:8269729821,deployment,github:GithubRun:1:3022301033,SUCCESS,DONE,,PRODUCTION,991,2022-09-09T12:13:58.000+00:00,2022-09-09T12:30:29.000+00:00, -github:GithubJob:1:8269872730,deploy,github:GithubRun:1:3022348786,SUCCESS,DONE,,PRODUCTION,1006,2022-09-09T12:21:30.000+00:00,2022-09-09T12:38:16.000+00:00, diff --git a/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks_prefix.csv b/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks_prefix.csv deleted file mode 100644 index deb5f3c8f05..00000000000 --- a/plugins/dora/e2e/snapshot_tables/lake_cicd_tasks_prefix.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,pipeline_id,result,status,type,environment,duration_sec,started_date,finished_date,cicd_scope_id -jenkins:GithubJob:1:8269872730,deploy,jenkins:GithubRun:1:3022348786,SUCCESS,DONE,,PRODUCTION,1006,2022-09-09T12:21:30.000+00:00,2022-09-09T12:38:16.000+00:00, diff --git a/plugins/dora/tasks/cicd_task_env_enricher.go b/plugins/dora/tasks/cicd_task_env_enricher.go index 18400aead99..5e1c424e9c0 100644 --- a/plugins/dora/tasks/cicd_task_env_enricher.go +++ b/plugins/dora/tasks/cicd_task_env_enricher.go @@ -18,7 +18,6 @@ limitations under the License. package tasks import ( - "fmt" "reflect" "regexp" @@ -40,51 +39,30 @@ var EnrichTaskEnvMeta = core.SubTaskMeta{ func EnrichTasksEnv(taskCtx core.SubTaskContext) (err errors.Error) { db := taskCtx.GetDal() data := taskCtx.GetData().(*DoraTaskData) - repoId := data.Options.RepoId + projectName := data.Options.ProjectName productionNamePattern := data.Options.ProductionPattern - // TODO: STAGE 2 - // stagingNamePattern := data.Options.StagingPattern - // testingNamePattern := data.Options.TestingPattern - prefix := data.Options.Prefix - - productionNameRegexp, errRegexp := regexp.Compile(productionNamePattern) - if errRegexp != nil { - return errors.Default.Wrap(errRegexp, "Regexp compile productionPattern failed") + productionNameRegexp, err := errors.Convert01(regexp.Compile(productionNamePattern)) + if err != nil { + return err } - // TODO: STAGE 2 - // stagingNameRegexp, errRegexp := regexp.Compile(stagingNamePattern) - // if errRegexp != nil { - // return errors.Default.Wrap(errRegexp, "Regexp compile stagingPattern failed") - // } - // testingNameRegexp, errRegexp := regexp.Compile(testingNamePattern) - // if errRegexp != nil { - // return errors.Default.Wrap(errRegexp, "Regexp compile testingPattern failed") - // } - var cursor dal.Rows - if len(prefix) == 0 { - cursor, err = db.Cursor( - dal.From(&devops.CICDTask{}), - dal.Join("left join cicd_pipeline_commits cpr on cicd_tasks.pipeline_id = cpr.pipeline_id"), - dal.Where("status=? and repo_id=?", devops.DONE, repoId)) - } else { - likeString := fmt.Sprintf(`%s:%s`, prefix, "%") - cursor, err = db.Cursor( - dal.From(&devops.CICDTask{}), - dal.Where("status=? and id like ? ", devops.DONE, likeString)) - } + cursor, err := db.Cursor( + dal.From(`cicd_tasks ct`), + dal.Join("inner join project_mapping pm on pm.row_id = ct.cicd_scope_id and pm.table = ?", "cicd_scopes"), + dal.Where(`pm.project_name = ?`, projectName), + ) + if err != nil { return err } defer cursor.Close() - converter, err := helper.NewDataConverter(helper.DataConverterArgs{ RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ - Ctx: taskCtx, + Ctx: taskCtx, Params: DoraApiParams{ - // TODO + ProjectName: projectName, }, Table: "cicd_tasks", }, @@ -93,36 +71,14 @@ func EnrichTasksEnv(taskCtx core.SubTaskContext) (err errors.Error) { Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { cicdTask := inputRow.(*devops.CICDTask) results := make([]interface{}, 0, 1) - var EnvironmentVar string if productionNamePattern == "" { - EnvironmentVar = devops.PRODUCTION + cicdTask.Environment = devops.PRODUCTION } else { if productEnv := productionNameRegexp.FindString(cicdTask.Name); productEnv != "" { - EnvironmentVar = devops.PRODUCTION + cicdTask.Environment = devops.PRODUCTION } } - - // TODO: STAGE 2 - // if stagingEnv := stagingNameRegexp.FindString(cicdTask.Name); stagingEnv != "" { - // EnvironmentVar = devops.STAGING - // } - // if testingEnv := testingNameRegexp.FindString(cicdTask.Name); testingEnv != "" { - // EnvironmentVar = devops.TESTING - // } - - cicdPipelineFilter := &devops.CICDTask{ - DomainEntity: cicdTask.DomainEntity, - PipelineId: cicdTask.PipelineId, - Name: cicdTask.Name, - Type: cicdTask.Type, - Result: cicdTask.Result, - Status: cicdTask.Status, - DurationSec: cicdTask.DurationSec, - StartedDate: cicdTask.StartedDate, - FinishedDate: cicdTask.FinishedDate, - Environment: EnvironmentVar, - } - results = append(results, cicdPipelineFilter) + results = append(results, cicdTask) return results, nil }, }) diff --git a/plugins/helper/iso8601time.go b/plugins/helper/iso8601time.go index 8d4092626d6..564d7f739ed 100644 --- a/plugins/helper/iso8601time.go +++ b/plugins/helper/iso8601time.go @@ -18,6 +18,7 @@ limitations under the License. package helper import ( + "database/sql/driver" "fmt" "regexp" "strings" @@ -63,6 +64,14 @@ func init() { Matcher: regexp.MustCompile(`[+-][\d]{2}:[\d]{2}$`), Format: "2006-01-02T15:04:05-07:00", }, + { + Matcher: regexp.MustCompile(`[\d]{4}-[\d]{2}-[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}$`), + Format: "2006-01-02 15:04:05", + }, + { + Matcher: regexp.MustCompile(`[+-][\d]{2}-[\d]{2}$`), + Format: "2006-01-02", + }, } } @@ -91,6 +100,9 @@ func (jt *Iso8601Time) UnmarshalJSON(b []byte) error { if timeString == "null" { return nil } + if strings.Contains(timeString, "0000-00-00") { + return nil + } timeString = strings.Trim(timeString, `"`) t, err := ConvertStringToTime(timeString) if err != nil { @@ -131,3 +143,29 @@ func Iso8601TimeToTime(iso8601Time *Iso8601Time) *time.Time { t := iso8601Time.ToTime() return &t } + +// Value FIXME ... +func (jt *Iso8601Time) Value() (driver.Value, error) { + if jt == nil { + return nil, nil + } + var zeroTime time.Time + t := jt.time + if t.UnixNano() == zeroTime.UnixNano() { + return nil, nil + } + return t, nil +} + +// Scan FIXME ... +func (jt *Iso8601Time) Scan(v interface{}) error { + value, ok := v.(time.Time) + if ok { + *jt = Iso8601Time{ + time: value, + format: time.RFC3339, + } + return nil + } + return fmt.Errorf("can not convert %v to timestamp", v) +} diff --git a/plugins/zentao/api/blueprint.go b/plugins/zentao/api/blueprint.go new file mode 100644 index 00000000000..942de5b6963 --- /dev/null +++ b/plugins/zentao/api/blueprint.go @@ -0,0 +1,70 @@ +/* +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. +*/ + +package api + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func MakePipelinePlan(subtaskMetas []core.SubTaskMeta, connectionId uint64, scope []*core.BlueprintScopeV100) (core.PipelinePlan, errors.Error) { + var err error + plan := make(core.PipelinePlan, len(scope)) + for i, scopeElem := range scope { + taskOptions := make(map[string]interface{}) + err = json.Unmarshal(scopeElem.Options, &taskOptions) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + taskOptions["connectionId"] = connectionId + + //TODO Add transformation rules to task options + + /* + var transformationRules tasks.TransformationRules + if len(scopeElem.Transformation) > 0 { + err = json.Unmarshal(scopeElem.Transformation, &transformationRules) + if err != nil { + return nil, err + } + } + */ + //taskOptions["transformationRules"] = transformationRules + _, err := tasks.DecodeAndValidateTaskOptions(taskOptions) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + // subtasks + subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, scopeElem.Entities) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + plan[i] = core.PipelineStage{ + { + Plugin: "zentao", + Subtasks: subtasks, + Options: taskOptions, + }, + } + } + return plan, nil +} diff --git a/plugins/zentao/api/connection.go b/plugins/zentao/api/connection.go new file mode 100644 index 00000000000..93244c5e150 --- /dev/null +++ b/plugins/zentao/api/connection.go @@ -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. +*/ + +package api + +import ( + "context" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/mitchellh/mapstructure" + "net/http" +) + +// TODO Please modify the following code to fit your needs +func TestConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + // process input + var params models.TestConnectionRequest + err := mapstructure.Decode(input.Body, ¶ms) + if err != nil { + return nil, errors.BadInput.Wrap(err, "could not decode request parameters") + } + err = vld.Struct(params) + if err != nil { + return nil, errors.BadInput.Wrap(err, "could not validate request parameters") + } + + authApiClient, err := helper.NewApiClient(context.TODO(), params.Endpoint, nil, 0, params.Proxy, basicRes) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + + // request for access token + tokenReqBody := &models.ApiAccessTokenRequest{ + Account: params.Username, + Password: params.Password, + } + tokenRes, err := authApiClient.Post("/tokens", nil, tokenReqBody, nil) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + tokenResBody := &models.ApiAccessTokenResponse{} + err = helper.UnmarshalResponse(tokenRes, tokenResBody) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + if tokenResBody.Token == "" { + return nil, errors.Default.New("failed to request access token") + } + + // output + return nil, nil +} + +//TODO Please modify the folowing code to adapt to your plugin +/* +POST /plugins/Zentao/connections +{ + "name": "Zentao data connection name", + "endpoint": "Zentao api endpoint, i.e. https://example.com", + "username": "username, usually should be email address", + "password": "Zentao api access token" +} +*/ +func PostConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + // update from request and save to database + connection := &models.ZentaoConnection{} + err := connectionHelper.Create(connection, input) + if err != nil { + return nil, err + } + return &core.ApiResourceOutput{Body: connection, Status: http.StatusOK}, nil +} + +//TODO Please modify the folowing code to adapt to your plugin +/* +PATCH /plugins/Zentao/connections/:connectionId +{ + "name": "Zentao data connection name", + "endpoint": "Zentao api endpoint, i.e. https://example.com", + "username": "username, usually should be email address", + "password": "Zentao api access token" +} +*/ +func PatchConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + connection := &models.ZentaoConnection{} + err := connectionHelper.Patch(connection, input) + if err != nil { + return nil, err + } + return &core.ApiResourceOutput{Body: connection}, nil +} + +/* +DELETE /plugins/Zentao/connections/:connectionId +*/ +func DeleteConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + connection := &models.ZentaoConnection{} + err := connectionHelper.First(connection, input.Params) + if err != nil { + return nil, err + } + err = connectionHelper.Delete(connection) + return &core.ApiResourceOutput{Body: connection}, err +} + +/* +GET /plugins/Zentao/connections +*/ +func ListConnections(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + var connections []models.ZentaoConnection + err := connectionHelper.List(&connections) + if err != nil { + return nil, err + } + return &core.ApiResourceOutput{Body: connections, Status: http.StatusOK}, nil +} + +//TODO Please modify the folowing code to adapt to your plugin +/* +GET /plugins/Zentao/connections/:connectionId +{ + "name": "Zentao data connection name", + "endpoint": "Zentao api endpoint, i.e. https://merico.atlassian.net/rest", + "username": "username, usually should be email address", + "password": "Zentao api access token" +} +*/ +func GetConnection(input *core.ApiResourceInput) (*core.ApiResourceOutput, errors.Error) { + connection := &models.ZentaoConnection{} + err := connectionHelper.First(connection, input.Params) + return &core.ApiResourceOutput{Body: connection}, err +} diff --git a/plugins/zentao/api/init.go b/plugins/zentao/api/init.go new file mode 100644 index 00000000000..6774e148218 --- /dev/null +++ b/plugins/zentao/api/init.go @@ -0,0 +1,39 @@ +/* +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. +*/ + +package api + +import ( + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/go-playground/validator/v10" + "github.com/spf13/viper" + "gorm.io/gorm" +) + +var vld *validator.Validate +var connectionHelper *helper.ConnectionApiHelper +var basicRes core.BasicRes + +func Init(config *viper.Viper, logger core.Logger, database *gorm.DB) { + basicRes = helper.NewDefaultBasicRes(config, logger, database) + vld = validator.New() + connectionHelper = helper.NewConnectionHelper( + basicRes, + vld, + ) +} diff --git a/plugins/zentao/e2e/account_test.go b/plugins/zentao/e2e/account_test.go new file mode 100644 index 00000000000..67ec47667b7 --- /dev/null +++ b/plugins/zentao/e2e/account_test.go @@ -0,0 +1,63 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/crossdomain" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoAccountDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_accounts.csv", + "_raw_zentao_api_accounts") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoAccount{}) + dataflowTester.Subtask(tasks.ExtractAccountMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoAccount{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_accounts.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + dataflowTester.FlushTabler(&crossdomain.Account{}) + dataflowTester.Subtask(tasks.ConvertAccountMeta, taskData) + dataflowTester.VerifyTableWithOptions(&crossdomain.Account{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/users.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/bug_test.go b/plugins/zentao/e2e/bug_test.go new file mode 100644 index 00000000000..c7f9baadd36 --- /dev/null +++ b/plugins/zentao/e2e/bug_test.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoBugDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_bugs.csv", + "_raw_zentao_api_bugs") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoBug{}) + dataflowTester.Subtask(tasks.ExtractBugMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoBug{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_bugs.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + // verify conversion + dataflowTester.FlushTabler(&ticket.Issue{}) + dataflowTester.FlushTabler(&ticket.BoardIssue{}) + dataflowTester.Subtask(tasks.ConvertBugMeta, taskData) + dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/issues_bug.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + dataflowTester.VerifyTableWithOptions(&ticket.BoardIssue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/board_issues_bug.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/department_test.go b/plugins/zentao/e2e/department_test.go new file mode 100644 index 00000000000..c100da03424 --- /dev/null +++ b/plugins/zentao/e2e/department_test.go @@ -0,0 +1,63 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/crossdomain" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoDepartmentDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_departments.csv", + "_raw_zentao_api_departments") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoDepartment{}) + dataflowTester.Subtask(tasks.ExtractDepartmentMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoDepartment{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_departments.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + dataflowTester.FlushTabler(&crossdomain.Team{}) + dataflowTester.Subtask(tasks.ConvertDepartmentMeta, taskData) + dataflowTester.VerifyTableWithOptions(&crossdomain.Team{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/teams.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/execution_test.go b/plugins/zentao/e2e/execution_test.go new file mode 100644 index 00000000000..3b0371be53f --- /dev/null +++ b/plugins/zentao/e2e/execution_test.go @@ -0,0 +1,63 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoExecutionDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_executions.csv", + "_raw_zentao_api_executions") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoExecution{}) + dataflowTester.Subtask(tasks.ExtractExecutionMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoExecution{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_executions.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + dataflowTester.FlushTabler(&ticket.Board{}) + dataflowTester.Subtask(tasks.ConvertExecutionMeta, taskData) + dataflowTester.VerifyTableWithOptions(&ticket.Board{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/boards_execution.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/product_test.go b/plugins/zentao/e2e/product_test.go new file mode 100644 index 00000000000..af7ad288784 --- /dev/null +++ b/plugins/zentao/e2e/product_test.go @@ -0,0 +1,63 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoProductDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 9, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_products.csv", + "_raw_zentao_api_products") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoProduct{}) + dataflowTester.Subtask(tasks.ExtractProductMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoProduct{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_products.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + dataflowTester.FlushTabler(&ticket.Board{}) + dataflowTester.Subtask(tasks.ConvertProductMeta, taskData) + dataflowTester.VerifyTableWithOptions(&ticket.Board{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/boards_product.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv new file mode 100644 index 00000000000..f1aaf1229e6 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv @@ -0,0 +1,13 @@ +id,params,data,url,input,created_at +31,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +32,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +33,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +34,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +35,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +36,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +37,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +38,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +39,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +40,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +41,"{""ConnectionId"":3,""ProductId"":2,""ExecutionId"":1,""ProjectId"":3}","{""id"":11,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 +42,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":3,""ProjectId"":3}","{""id"":12,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv new file mode 100644 index 00000000000..d69bb712902 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv @@ -0,0 +1,7 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":4,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":11,""execution"":1,""plan"":0,""story"":4,""storyVersion"":1,""task"":9,""toTask"":0,""toStory"":0,""title"":""\u552e\u540e\u670d\u52a1\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u552e\u540e\u670d\u52a1\u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u4e71\u7801\u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793a\u003C\/p\u003E"",""status"":""resolved"",""subStatus"":"""",""color"":"""",""confirmed"":1,""activatedCount"":0,""activatedDate"":null,""feedbackBy"":"""",""notifyEmail"":"""",""mailto"":[],""openedBy"":{""id"":9,""account"":""tester3"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u4e19""},""openedDate"":""2012-06-05T03:00:19Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":{""id"":9,""account"":""tester3"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u4e19""},""assignedDate"":""2022-10-05T04:10:08Z"",""deadline"":null,""resolvedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""resolution"":""fixed"",""resolvedBuild"":""\u4e3b\u5e72"",""resolvedDate"":""2022-10-05T04:09:59Z"",""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":0,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":0,""lastEditedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""lastEditedDate"":""2022-10-05T04:10:08Z"",""deleted"":false,""priOrder"":""1"",""severityOrder"":3,""needconfirm"":false,""statusName"":""\u5df2\u89e3\u51b3"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 +2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":10,""execution"":1,""plan"":0,""story"":3,""storyVersion"":2,""task"":6,""toTask"":0,""toStory"":0,""title"":""\u6210\u679c\u5c55\u793a\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u6210\u679c\u5c55\u793a    \u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u4e71\u7801\u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793a\u003C\/p\u003E"",""status"":""active"",""subStatus"":"""",""color"":"""",""confirmed"":0,""activatedCount"":0,""activatedDate"":null,""feedbackBy"":"""",""notifyEmail"":"""",""mailto"":[],""openedBy"":{""id"":8,""account"":""tester2"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u4e59""},""openedDate"":""2012-06-05T02:58:22Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":{""id"":4,""account"":""dev1"",""avatar"":"""",""realname"":""\u5f00\u53d1\u7532""},""assignedDate"":""2012-06-05T02:58:22Z"",""deadline"":null,""resolvedBy"":null,""resolution"":"""",""resolvedBuild"":"""",""resolvedDate"":null,""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":0,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":0,""lastEditedBy"":null,""lastEditedDate"":""2021-04-28T03:09:08Z"",""deleted"":false,""priOrder"":""1"",""severityOrder"":3,""needconfirm"":false,""statusName"":""\u6fc0\u6d3b"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 +3,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u65b0\u95fb\u4e2d\u5fc3\u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u9875\u9762\u51fa\u73b0\u4e71\u7801\u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793arew\u003C\/p\u003E"",""status"":""delay"",""subStatus"":"""",""color"":"""",""confirmed"":1,""activatedCount"":1,""activatedDate"":""2022-10-05T04:16:44Z"",""feedbackBy"":"""",""notifyEmail"":""1114255335@qq.com"",""mailto"":[{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""}],""openedBy"":{""id"":7,""account"":""tester1"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7532""},""openedDate"":""2012-06-05T02:57:11Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":null,""assignedDate"":""2022-10-05T04:19:22Z"",""deadline"":""2022-10-06"",""resolvedBy"":null,""resolution"":"""",""resolvedBuild"":"""",""resolvedDate"":null,""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":3,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":1,""lastEditedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""lastEditedDate"":""2022-10-05T04:19:22Z"",""deleted"":false,""priOrder"":""2"",""severityOrder"":3,""delay"":42,""needconfirm"":false,""statusName"":""\u8fc7\u671fBug"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 +4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u9996\u9875\u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u51fa\u73b0\u4e71\u7801    \u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793a\u003C\/p\u003E"",""status"":""active"",""subStatus"":"""",""color"":"""",""confirmed"":0,""activatedCount"":0,""activatedDate"":null,""feedbackBy"":"""",""notifyEmail"":"""",""mailto"":[],""openedBy"":{""id"":7,""account"":""tester1"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7532""},""openedDate"":""2012-06-05T02:56:11Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":{""id"":4,""account"":""dev1"",""avatar"":"""",""realname"":""\u5f00\u53d1\u7532""},""assignedDate"":""2012-06-05T02:56:11Z"",""deadline"":null,""resolvedBy"":null,""resolution"":"""",""resolvedBuild"":"""",""resolvedDate"":null,""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":0,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":0,""lastEditedBy"":null,""lastEditedDate"":""2021-04-28T03:09:08Z"",""deleted"":false,""priOrder"":""1"",""severityOrder"":3,""needconfirm"":false,""statusName"":""\u6fc0\u6d3b"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 +5,"{""ConnectionId"":2,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":6,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u65b0\u95fb\u4e2d\u5fc3\u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u9875\u9762\u51fa\u73b0\u4e71\u7801\u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793arew\u003C\/p\u003E"",""status"":""delay"",""subStatus"":"""",""color"":"""",""confirmed"":1,""activatedCount"":1,""activatedDate"":""2022-10-05T04:16:44Z"",""feedbackBy"":"""",""notifyEmail"":""1114255335@qq.com"",""mailto"":[{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""}],""openedBy"":{""id"":7,""account"":""tester1"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7532""},""openedDate"":""2012-06-05T02:57:11Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":null,""assignedDate"":""2022-10-05T04:19:22Z"",""deadline"":""2022-10-06"",""resolvedBy"":null,""resolution"":"""",""resolvedBuild"":"""",""resolvedDate"":null,""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":3,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":1,""lastEditedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""lastEditedDate"":""2022-10-05T04:19:22Z"",""deleted"":false,""priOrder"":""2"",""severityOrder"":3,""delay"":42,""needconfirm"":false,""statusName"":""\u8fc7\u671fBug"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 +6,"{""ConnectionId"":3,""ProductId"":3,""ExecutionId"":11,""ProjectId"":1}","{""id"":5,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u9996\u9875\u003C\/p\u003E\n\u003Cp\u003E[\u7ed3\u679c]\u51fa\u73b0\u4e71\u7801    \u003C\/p\u003E\n\u003Cp\u003E[\u671f\u671b]\u6b63\u5e38\u663e\u793a\u003C\/p\u003E"",""status"":""active"",""subStatus"":"""",""color"":"""",""confirmed"":0,""activatedCount"":0,""activatedDate"":null,""feedbackBy"":"""",""notifyEmail"":"""",""mailto"":[],""openedBy"":{""id"":7,""account"":""tester1"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7532""},""openedDate"":""2012-06-05T02:56:11Z"",""openedBuild"":""\u4e3b\u5e72"",""assignedTo"":{""id"":4,""account"":""dev1"",""avatar"":"""",""realname"":""\u5f00\u53d1\u7532""},""assignedDate"":""2012-06-05T02:56:11Z"",""deadline"":null,""resolvedBy"":null,""resolution"":"""",""resolvedBuild"":"""",""resolvedDate"":null,""closedBy"":null,""closedDate"":null,""duplicateBug"":0,""linkBug"":"""",""case"":0,""caseVersion"":1,""feedback"":0,""result"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""repoType"":"""",""issueKey"":"""",""testtask"":0,""lastEditedBy"":null,""lastEditedDate"":""2021-04-28T03:09:08Z"",""deleted"":false,""priOrder"":""1"",""severityOrder"":3,""needconfirm"":false,""statusName"":""\u6fc0\u6d3b"",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,null,2022-11-17 06:28:36.551 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv new file mode 100644 index 00000000000..88bd599d549 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv @@ -0,0 +1,13 @@ +id,params,data,url,input,created_at +31,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +32,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +33,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +34,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +35,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +36,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +37,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +38,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +39,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +40,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +41,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":4,""ProjectId"":3}","{""id"":12,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 +42,"{""ConnectionId"":3,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":11,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv new file mode 100644 index 00000000000..500d4a4c9c6 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv @@ -0,0 +1,4 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":12,""ProjectId"":1}","{""id"":12,""project"":1091,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":1091,""path"":"",1091,12,"",""grade"":1,""name"":""TR5"",""code"":""0.1.3"",""begin"":""2022-11-01"",""end"":""2022-11-03"",""realBegan"":""2022-07-07"",""realEnd"":null,""days"":"""",""status"":""done"",""subStatus"":"""",""pri"":""1"",""desc"":"""",""version"":0,""parentVersion"":0,""planDuration"":24,""realDuration"":0,""openedBy"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""openedDate"":""2021-05-27T07:16:59Z"",""openedVersion"":""15.0.rc3"",""lastEditedBy"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""lastEditedDate"":""2022-11-15T08:22:09Z"",""closedBy"":null,""closedDate"":null,""canceledBy"":null,""canceledDate"":null,""suspendedDate"":""0000-00-00"",""PO"":null,""PM"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""QD"":null,""RD"":null,""team"":""Windows\u7ec4"",""acl"":""open"",""whitelist"":[],""order"":5,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""1"",""deleted"":false,""isSync"":0,""projectReviewID"":0,""totalHours"":0,""totalEstimate"":8411,""totalConsumed"":11564.5,""totalLeft"":0,""projectInfo"":{""id"":1091,""project"":0,""model"":""scrum"",""type"":""project"",""lifetime"":"""",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":""extend"",""parent"":629,""path"":"",629,1091,"",""grade"":2,""name"":""windows\u57fa\u7ebf\u9879\u76ee\u3010\u57fa\u7ebf\u3011"",""code"":""9050237-10"",""begin"":""2022-11-01"",""end"":""2059-12-31"",""realBegan"":""2022-11-01"",""realEnd"":""0000-00-00"",""days"":0,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":""Windows \u57fa\u7ebf\u5f00\u53d1\uff0cBUG\u7ba1\u7406\uff0c\u7248\u672c\u8ba1\u5212\uff0c\u8fed\u4ee3\u7ba1\u7406"",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":""c23900"",""openedDate"":""2022-11-01 11:04:06"",""openedVersion"":"""",""lastEditedBy"":""h24926"",""lastEditedDate"":""2022-11-07 19:16:21"",""closedBy"":"""",""closedDate"":""0000-00-00 00:00:00"",""canceledBy"":"""",""canceledDate"":""0000-00-00 00:00:00"",""suspendedDate"":""0000-00-00"",""PO"":"""",""PM"":""y24914"",""QD"":"""",""RD"":"""",""team"":""windows\u57fa\u7ebf\u9879\u76ee\u3010\u57fa\u7ebf\u3011"",""acl"":""program"",""whitelist"":"",c23900"",""order"":4625,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":""0"",""isSync"":1,""projectReviewID"":0},""progress"":100,""teamMembers"":[{""id"":174677,""root"":12,""type"":""execution"",""account"":""a29123"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":280,""realname"":""aimiaomiao 29123""},{""id"":174678,""root"":12,""type"":""execution"",""account"":""c23900"",""role"":""\u7814\u53d1\u4e3b\u7ba1"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":21,""realname"":""chenhongyin 23900""},{""id"":174679,""root"":12,""type"":""execution"",""account"":""c25333"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":28,""realname"":""chenqiufeng 25333""},{""id"":174680,""root"":12,""type"":""execution"",""account"":""c25582"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-09-29"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":31,""realname"":""chenruihuang 25582""},{""id"":174730,""root"":12,""type"":""execution"",""account"":""d25648"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-14"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":35,""realname"":""daizhou 25648""},{""id"":174681,""root"":12,""type"":""execution"",""account"":""d26823"",""role"":""\u914d\u7f6e\u7ba1\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-01"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":54,""realname"":""dengjiqing 26823""},{""id"":174682,""root"":12,""type"":""execution"",""account"":""h24926"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":17,""realname"":""hexiuzhen 24926""},{""id"":174683,""root"":12,""type"":""execution"",""account"":""h25203"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":8,""realname"":""huanggaolin 25203""},{""id"":174684,""root"":12,""type"":""execution"",""account"":""h25257"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":19,""realname"":""heheshan 25257""},{""id"":174685,""root"":12,""type"":""execution"",""account"":""h25914"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":39,""realname"":""heguifang 25914""},{""id"":174686,""root"":12,""type"":""execution"",""account"":""h26832"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-14"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":67,""realname"":""huliangliang 26832""},{""id"":174687,""root"":12,""type"":""execution"",""account"":""h27440"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-10"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":61,""realname"":""hezhongmo 27440""},{""id"":174688,""root"":12,""type"":""execution"",""account"":""h27445"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-19"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":62,""realname"":""hanxiaoyin 27445""},{""id"":174689,""root"":12,""type"":""execution"",""account"":""h27760"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":271,""realname"":""hexiaoli 27760""},{""id"":174690,""root"":12,""type"":""execution"",""account"":""h27946"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-08-05"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":164,""realname"":""huanghuamin 27946""},{""id"":174691,""root"":12,""type"":""execution"",""account"":""j26755"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-10-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":53,""realname"":""jinshuyi 26755""},{""id"":174692,""root"":12,""type"":""execution"",""account"":""j26993"",""role"":""\u6d4b\u8bd5\u4e3b\u7ba1"",""position"":"""",""limited"":""no"",""join"":""2021-10-20"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":56,""realname"":""jinlan 26993""},{""id"":174693,""root"":12,""type"":""execution"",""account"":""k24711"",""role"":""SE\u7cfb\u7edf\u5de5\u7a0b\u5e08"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":23,""realname"":""kangshuo 24711""},{""id"":174694,""root"":12,""type"":""execution"",""account"":""k27312"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":151,""realname"":""kegenxian 27312""},{""id"":174695,""root"":12,""type"":""execution"",""account"":""l24783"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":3,""realname"":""lichuangjin 24783""},{""id"":174696,""root"":12,""type"":""execution"",""account"":""l24895"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":16,""realname"":""linzejia 24895""},{""id"":174697,""root"":12,""type"":""execution"",""account"":""l25733"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":41,""realname"":""linsixia 25733""},{""id"":174698,""root"":12,""type"":""execution"",""account"":""l25916"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":65,""realname"":""liuzhichao 25916""},{""id"":174699,""root"":12,""type"":""execution"",""account"":""l27187"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":59,""realname"":""lijian 27187""},{""id"":174700,""root"":12,""type"":""execution"",""account"":""l28380"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-05"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":156,""realname"":""liukunle 28380""},{""id"":174701,""root"":12,""type"":""execution"",""account"":""l28768"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-04"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":272,""realname"":""lipeiying 28768""},{""id"":174702,""root"":12,""type"":""execution"",""account"":""l29004"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-22"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":208,""realname"":""liuyaqing 29004""},{""id"":174703,""root"":12,""type"":""execution"",""account"":""l29212"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":238,""realname"":""leiqizheng 29212""},{""id"":174704,""root"":12,""type"":""execution"",""account"":""l30115"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-09-30"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":328,""realname"":""linzhiyao 30115""},{""id"":174705,""root"":12,""type"":""execution"",""account"":""n27493"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-19"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":63,""realname"":""ninghengyi 27493""},{""id"":174706,""root"":12,""type"":""execution"",""account"":""p24957"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":7,""realname"":""panwangji 24957""},{""id"":174707,""root"":12,""type"":""execution"",""account"":""p29092"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-22"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":209,""realname"":""pengqiaomei 29092""},{""id"":174708,""root"":12,""type"":""execution"",""account"":""t25220"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":9,""realname"":""tanzhiwei 25220""},{""id"":174709,""root"":12,""type"":""execution"",""account"":""t28447"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":137,""realname"":""tangshuai 28447""},{""id"":174710,""root"":12,""type"":""execution"",""account"":""t28534"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-09-29"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":143,""realname"":""tangjia 28534""},{""id"":174711,""root"":12,""type"":""execution"",""account"":""w25487"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-06-18"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":30,""realname"":""wutangyuan 25487""},{""id"":174712,""root"":12,""type"":""execution"",""account"":""w26230"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":46,""realname"":""wupeifeng 26230""},{""id"":174713,""root"":12,""type"":""execution"",""account"":""w26756"",""role"":""\u914d\u7f6e\u7ba1\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-01"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":52,""realname"":""wuxueting 26756""},{""id"":174714,""root"":12,""type"":""execution"",""account"":""w27907"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-08"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":147,""realname"":""wuwenting 27907""},{""id"":174715,""root"":12,""type"":""execution"",""account"":""w28642"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-08"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":159,""realname"":""wangjiankai 28642""},{""id"":174716,""root"":12,""type"":""execution"",""account"":""x24743"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":13,""realname"":""xulinmeng 24743""},{""id"":174717,""root"":12,""type"":""execution"",""account"":""x25784"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":66,""realname"":""xianruncheng 25784""},{""id"":174718,""root"":12,""type"":""execution"",""account"":""x28275"",""role"":""PDT\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-10-18"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":251,""realname"":""xiemeng 28275""},{""id"":174719,""root"":12,""type"":""execution"",""account"":""x28530"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":148,""realname"":""xuyajing 28530""},{""id"":174720,""root"":12,""type"":""execution"",""account"":""x29245"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-08-09"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":267,""realname"":""xiaoyalun 29245""},{""id"":174721,""root"":12,""type"":""execution"",""account"":""y24914"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":6,""realname"":""yudong 24914""},{""id"":174722,""root"":12,""type"":""execution"",""account"":""y26925"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-10-12"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":55,""realname"":""yangzhiyu 26925""},{""id"":174723,""root"":12,""type"":""execution"",""account"":""y27441"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-01-07"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":60,""realname"":""yuanhaigan 27441""},{""id"":174724,""root"":12,""type"":""execution"",""account"":""y28116"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-07"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":123,""realname"":""yaowei 28116""},{""id"":174725,""root"":12,""type"":""execution"",""account"":""y28520"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":254,""realname"":""yanghonghong 28520""},{""id"":174726,""root"":12,""type"":""execution"",""account"":""y29058"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-25"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":224,""realname"":""yangtianbao 29058""},{""id"":174727,""root"":12,""type"":""execution"",""account"":""y29292"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-25"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":232,""realname"":""yangwei 29292""},{""id"":174728,""root"":12,""type"":""execution"",""account"":""z25182"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":12,""realname"":""zhongmin 25182""},{""id"":174729,""root"":12,""type"":""execution"",""account"":""z27014"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-10-20"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":57,""realname"":""zhuowenpei 27014""}],""products"":[{""id"":272,""name"":""\u4e91\u5c4fWindows\u5e94\u7528"",""plans"":[]}],""caseReview"":false}",http://10.214.64.12/api.php/v1/executions/12?limit=100&page=1,null,2022-11-21 17:29:50 +2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days"":20,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":""\u5f00\u53d1\u4f01\u4e1a\u7f51\u7ad9\u7684\u57fa\u672c\u96cf\u5f62\u3002\u003Cbr \/\u003E"",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":null,""openedDate"":null,""openedVersion"":"""",""lastEditedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""lastEditedDate"":""2022-11-21T06:00:56Z"",""closedBy"":null,""closedDate"":null,""canceledBy"":null,""canceledDate"":null,""suspendedDate"":""0000-00-00"",""PO"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""PM"":{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""},""QD"":{""id"":10,""account"":""testManager"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7ecf\u7406""},""RD"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""team"":""\u516c\u53f8\u5f00\u53d1\u56e2\u961f"",""acl"":""open"",""whitelist"":[],""order"":5,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":false,""delay"":173,""totalHours"":11753,""totalEstimate"":52,""totalConsumed"":51.5,""totalLeft"":27.5,""projectInfo"":{""id"":7,""project"":0,""model"":""scrum"",""type"":""project"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":""extend"",""parent"":6,""path"":"",6,7,"",""grade"":2,""name"":""\u4f01\u4e1a\u7ba1\u7406\u7cfb\u7edf"",""code"":"""",""begin"":""2021-06-05"",""end"":""2022-06-04"",""realBegan"":""0000-00-00"",""realEnd"":""0000-00-00"",""days"":0,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":"""",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":""admin"",""openedDate"":""2021-04-28 11:22:04"",""openedVersion"":""15.0.rc3"",""lastEditedBy"":""admin"",""lastEditedDate"":""2021-04-28 11:22:04"",""closedBy"":"""",""closedDate"":""0000-00-00 00:00:00"",""canceledBy"":"""",""canceledDate"":""0000-00-00 00:00:00"",""suspendedDate"":""0000-00-00"",""PO"":"""",""PM"":""projectManager"",""QD"":"""",""RD"":"""",""team"":"""",""acl"":""open"",""whitelist"":"",tester3,admin"",""order"":20,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":""0""},""progress"":65.2,""teamMembers"":[{""id"":2,""root"":1,""type"":""execution"",""account"":""productManager"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":2,""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":13,""root"":1,""type"":""execution"",""account"":""projectManager"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":3,""realname"":""\u9879\u76ee\u7ecf\u7406""},{""id"":11,""root"":1,""type"":""execution"",""account"":""dev1"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":4,""realname"":""\u5f00\u53d1\u7532""},{""id"":12,""root"":1,""type"":""execution"",""account"":""dev2"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":5,""realname"":""\u5f00\u53d1\u4e59""},{""id"":1,""root"":1,""type"":""execution"",""account"":""dev3"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":6,""realname"":""\u5f00\u53d1\u4e19""},{""id"":4,""root"":1,""type"":""execution"",""account"":""tester1"",""role"":""\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":7,""realname"":""\u6d4b\u8bd5\u7532""},{""id"":3,""root"":1,""type"":""execution"",""account"":""tester2"",""role"":""\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":8,""realname"":""\u6d4b\u8bd5\u4e59""},{""id"":16,""root"":1,""type"":""execution"",""account"":""testManager"",""role"":""\u6d4b\u8bd5\u8d1f\u8d23\u4eba"",""position"":"""",""limited"":""no"",""join"":""2021-04-28"",""days"":391,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":2737,""userID"":10,""realname"":""\u6d4b\u8bd5\u7ecf\u7406""}],""products"":[{""id"":1,""name"":""\u516c\u53f8\u4f01\u4e1a\u7f51\u7ad9\u5efa\u8bbe"",""plans"":[]}],""caseReview"":false}",http://iwater.red:8000/api.php/v1/executions/1?limit=100&page=1,null,2022-11-21 10:04:23.035 +3,"{""ConnectionId"":2,""ProductId"":4,""ExecutionId"":1,""ProjectId"":1}","{""id"":11,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days"":20,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":""\u5f00\u53d1\u4f01\u4e1a\u7f51\u7ad9\u7684\u57fa\u672c\u96cf\u5f62\u3002\u003Cbr \/\u003E"",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":null,""openedDate"":null,""openedVersion"":"""",""lastEditedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""lastEditedDate"":""2022-11-21T06:00:56Z"",""closedBy"":null,""closedDate"":null,""canceledBy"":null,""canceledDate"":null,""suspendedDate"":""0000-00-00"",""PO"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""PM"":{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""},""QD"":{""id"":10,""account"":""testManager"",""avatar"":"""",""realname"":""\u6d4b\u8bd5\u7ecf\u7406""},""RD"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""team"":""\u516c\u53f8\u5f00\u53d1\u56e2\u961f"",""acl"":""open"",""whitelist"":[],""order"":5,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":false,""delay"":173,""totalHours"":11753,""totalEstimate"":52,""totalConsumed"":51.5,""totalLeft"":27.5,""projectInfo"":{""id"":7,""project"":0,""model"":""scrum"",""type"":""project"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":""extend"",""parent"":6,""path"":"",6,7,"",""grade"":2,""name"":""\u4f01\u4e1a\u7ba1\u7406\u7cfb\u7edf"",""code"":"""",""begin"":""2021-06-05"",""end"":""2022-06-04"",""realBegan"":""0000-00-00"",""realEnd"":""0000-00-00"",""days"":0,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":"""",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":""admin"",""openedDate"":""2021-04-28 11:22:04"",""openedVersion"":""15.0.rc3"",""lastEditedBy"":""admin"",""lastEditedDate"":""2021-04-28 11:22:04"",""closedBy"":"""",""closedDate"":""0000-00-00 00:00:00"",""canceledBy"":"""",""canceledDate"":""0000-00-00 00:00:00"",""suspendedDate"":""0000-00-00"",""PO"":"""",""PM"":""projectManager"",""QD"":"""",""RD"":"""",""team"":"""",""acl"":""open"",""whitelist"":"",tester3,admin"",""order"":20,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":""0""},""progress"":65.2,""teamMembers"":[{""id"":2,""root"":1,""type"":""execution"",""account"":""productManager"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":2,""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":13,""root"":1,""type"":""execution"",""account"":""projectManager"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":3,""realname"":""\u9879\u76ee\u7ecf\u7406""},{""id"":11,""root"":1,""type"":""execution"",""account"":""dev1"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":4,""realname"":""\u5f00\u53d1\u7532""},{""id"":12,""root"":1,""type"":""execution"",""account"":""dev2"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":5,""realname"":""\u5f00\u53d1\u4e59""},{""id"":1,""root"":1,""type"":""execution"",""account"":""dev3"",""role"":""\u7814\u53d1"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":6,""realname"":""\u5f00\u53d1\u4e19""},{""id"":4,""root"":1,""type"":""execution"",""account"":""tester1"",""role"":""\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":7,""realname"":""\u6d4b\u8bd5\u7532""},{""id"":3,""root"":1,""type"":""execution"",""account"":""tester2"",""role"":""\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2013-02-20"",""days"":184,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":1288,""userID"":8,""realname"":""\u6d4b\u8bd5\u4e59""},{""id"":16,""root"":1,""type"":""execution"",""account"":""testManager"",""role"":""\u6d4b\u8bd5\u8d1f\u8d23\u4eba"",""position"":"""",""limited"":""no"",""join"":""2021-04-28"",""days"":391,""hours"":7,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":2737,""userID"":10,""realname"":""\u6d4b\u8bd5\u7ecf\u7406""}],""products"":[{""id"":1,""name"":""\u516c\u53f8\u4f01\u4e1a\u7f51\u7ad9\u5efa\u8bbe"",""plans"":[]}],""caseReview"":false}",http://iwater.red:8000/api.php/v1/executions/1?limit=100&page=1,null,2022-11-21 10:04:23.035 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv new file mode 100644 index 00000000000..9c755cee3fc --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv @@ -0,0 +1,2 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":1091,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":1091,""path"":"",1091,12,"",""grade"":1,""name"":""TR5"",""code"":""0.1.3"",""begin"":""2022-11-01"",""end"":""2022-11-03"",""realBegan"":""2022-07-07"",""realEnd"":null,""days"":0,""status"":""done"",""subStatus"":"""",""pri"":""1"",""desc"":"""",""version"":0,""parentVersion"":0,""planDuration"":24,""realDuration"":0,""openedBy"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""openedDate"":""2021-05-27T07:16:59Z"",""openedVersion"":""15.0.rc3"",""lastEditedBy"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""lastEditedDate"":""2022-11-15T08:22:09Z"",""closedBy"":null,""closedDate"":null,""canceledBy"":null,""canceledDate"":null,""suspendedDate"":""0000-00-00"",""PO"":null,""PM"":{""id"":6,""account"":""y24914"",""avatar"":""\/data\/upload\/1\/202208\/14105244013779o7"",""realname"":""yudong 24914""},""QD"":null,""RD"":null,""team"":""Windows\u7ec4"",""acl"":""open"",""whitelist"":[],""order"":5,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""1"",""deleted"":false,""isSync"":0,""projectReviewID"":0,""totalHours"":0,""totalEstimate"":8411,""totalConsumed"":11564.5,""totalLeft"":0,""projectInfo"":{""id"":1091,""project"":0,""model"":""scrum"",""type"":""project"",""lifetime"":"""",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":""extend"",""parent"":629,""path"":"",629,1091,"",""grade"":2,""name"":""windows\u57fa\u7ebf\u9879\u76ee\u3010\u57fa\u7ebf\u3011"",""code"":""9050237-10"",""begin"":""2022-11-01"",""end"":""2059-12-31"",""realBegan"":""2022-11-01"",""realEnd"":""0000-00-00"",""days"":0,""status"":""doing"",""subStatus"":"""",""pri"":""1"",""desc"":""Windows \u57fa\u7ebf\u5f00\u53d1\uff0cBUG\u7ba1\u7406\uff0c\u7248\u672c\u8ba1\u5212\uff0c\u8fed\u4ee3\u7ba1\u7406"",""version"":0,""parentVersion"":0,""planDuration"":0,""realDuration"":0,""openedBy"":""c23900"",""openedDate"":""2022-11-01 11:04:06"",""openedVersion"":"""",""lastEditedBy"":""h24926"",""lastEditedDate"":""2022-11-07 19:16:21"",""closedBy"":"""",""closedDate"":""0000-00-00 00:00:00"",""canceledBy"":"""",""canceledDate"":""0000-00-00 00:00:00"",""suspendedDate"":""0000-00-00"",""PO"":"""",""PM"":""y24914"",""QD"":"""",""RD"":"""",""team"":""windows\u57fa\u7ebf\u9879\u76ee\u3010\u57fa\u7ebf\u3011"",""acl"":""program"",""whitelist"":"",c23900"",""order"":4625,""vision"":""rnd"",""displayCards"":0,""fluidBoard"":""0"",""deleted"":""0"",""isSync"":1,""projectReviewID"":0},""progress"":100,""teamMembers"":[{""id"":174677,""root"":12,""type"":""execution"",""account"":""a29123"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":280,""realname"":""aimiaomiao 29123""},{""id"":174678,""root"":12,""type"":""execution"",""account"":""c23900"",""role"":""\u7814\u53d1\u4e3b\u7ba1"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":21,""realname"":""chenhongyin 23900""},{""id"":174679,""root"":12,""type"":""execution"",""account"":""c25333"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":28,""realname"":""chenqiufeng 25333""},{""id"":174680,""root"":12,""type"":""execution"",""account"":""c25582"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-09-29"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":31,""realname"":""chenruihuang 25582""},{""id"":174730,""root"":12,""type"":""execution"",""account"":""d25648"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-14"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":35,""realname"":""daizhou 25648""},{""id"":174681,""root"":12,""type"":""execution"",""account"":""d26823"",""role"":""\u914d\u7f6e\u7ba1\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-01"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":54,""realname"":""dengjiqing 26823""},{""id"":174682,""root"":12,""type"":""execution"",""account"":""h24926"",""role"":""\u9879\u76ee\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":17,""realname"":""hexiuzhen 24926""},{""id"":174683,""root"":12,""type"":""execution"",""account"":""h25203"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":8,""realname"":""huanggaolin 25203""},{""id"":174684,""root"":12,""type"":""execution"",""account"":""h25257"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":19,""realname"":""heheshan 25257""},{""id"":174685,""root"":12,""type"":""execution"",""account"":""h25914"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":39,""realname"":""heguifang 25914""},{""id"":174686,""root"":12,""type"":""execution"",""account"":""h26832"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-14"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":67,""realname"":""huliangliang 26832""},{""id"":174687,""root"":12,""type"":""execution"",""account"":""h27440"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-10"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":61,""realname"":""hezhongmo 27440""},{""id"":174688,""root"":12,""type"":""execution"",""account"":""h27445"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-19"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":62,""realname"":""hanxiaoyin 27445""},{""id"":174689,""root"":12,""type"":""execution"",""account"":""h27760"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":271,""realname"":""hexiaoli 27760""},{""id"":174690,""root"":12,""type"":""execution"",""account"":""h27946"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-08-05"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":164,""realname"":""huanghuamin 27946""},{""id"":174691,""root"":12,""type"":""execution"",""account"":""j26755"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-10-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":53,""realname"":""jinshuyi 26755""},{""id"":174692,""root"":12,""type"":""execution"",""account"":""j26993"",""role"":""\u6d4b\u8bd5\u4e3b\u7ba1"",""position"":"""",""limited"":""no"",""join"":""2021-10-20"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":56,""realname"":""jinlan 26993""},{""id"":174693,""root"":12,""type"":""execution"",""account"":""k24711"",""role"":""SE\u7cfb\u7edf\u5de5\u7a0b\u5e08"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":23,""realname"":""kangshuo 24711""},{""id"":174694,""root"":12,""type"":""execution"",""account"":""k27312"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":151,""realname"":""kegenxian 27312""},{""id"":174695,""root"":12,""type"":""execution"",""account"":""l24783"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":3,""realname"":""lichuangjin 24783""},{""id"":174696,""root"":12,""type"":""execution"",""account"":""l24895"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":16,""realname"":""linzejia 24895""},{""id"":174697,""root"":12,""type"":""execution"",""account"":""l25733"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":41,""realname"":""linsixia 25733""},{""id"":174698,""root"":12,""type"":""execution"",""account"":""l25916"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":65,""realname"":""liuzhichao 25916""},{""id"":174699,""root"":12,""type"":""execution"",""account"":""l27187"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":59,""realname"":""lijian 27187""},{""id"":174700,""root"":12,""type"":""execution"",""account"":""l28380"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-05"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":156,""realname"":""liukunle 28380""},{""id"":174701,""root"":12,""type"":""execution"",""account"":""l28768"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-04"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":272,""realname"":""lipeiying 28768""},{""id"":174702,""root"":12,""type"":""execution"",""account"":""l29004"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-22"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":208,""realname"":""liuyaqing 29004""},{""id"":174703,""root"":12,""type"":""execution"",""account"":""l29212"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":238,""realname"":""leiqizheng 29212""},{""id"":174704,""root"":12,""type"":""execution"",""account"":""l30115"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-09-30"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":328,""realname"":""linzhiyao 30115""},{""id"":174705,""root"":12,""type"":""execution"",""account"":""n27493"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-01-19"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":63,""realname"":""ninghengyi 27493""},{""id"":174706,""root"":12,""type"":""execution"",""account"":""p24957"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":7,""realname"":""panwangji 24957""},{""id"":174707,""root"":12,""type"":""execution"",""account"":""p29092"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-22"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":209,""realname"":""pengqiaomei 29092""},{""id"":174708,""root"":12,""type"":""execution"",""account"":""t25220"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":9,""realname"":""tanzhiwei 25220""},{""id"":174709,""root"":12,""type"":""execution"",""account"":""t28447"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":137,""realname"":""tangshuai 28447""},{""id"":174710,""root"":12,""type"":""execution"",""account"":""t28534"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-09-29"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":143,""realname"":""tangjia 28534""},{""id"":174711,""root"":12,""type"":""execution"",""account"":""w25487"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-06-18"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":30,""realname"":""wutangyuan 25487""},{""id"":174712,""root"":12,""type"":""execution"",""account"":""w26230"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":46,""realname"":""wupeifeng 26230""},{""id"":174713,""root"":12,""type"":""execution"",""account"":""w26756"",""role"":""\u914d\u7f6e\u7ba1\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-07-01"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":52,""realname"":""wuxueting 26756""},{""id"":174714,""root"":12,""type"":""execution"",""account"":""w27907"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-08"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":147,""realname"":""wuwenting 27907""},{""id"":174715,""root"":12,""type"":""execution"",""account"":""w28642"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-08"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":159,""realname"":""wangjiankai 28642""},{""id"":174716,""root"":12,""type"":""execution"",""account"":""x24743"",""role"":""\u5b89\u5353\u7cfb\u7edf\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":13,""realname"":""xulinmeng 24743""},{""id"":174717,""root"":12,""type"":""execution"",""account"":""x25784"",""role"":"""",""position"":"""",""limited"":""no"",""join"":""2022-02-11"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":66,""realname"":""xianruncheng 25784""},{""id"":174718,""root"":12,""type"":""execution"",""account"":""x28275"",""role"":""PDT\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-10-18"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":251,""realname"":""xiemeng 28275""},{""id"":174719,""root"":12,""type"":""execution"",""account"":""x28530"",""role"":""\u4ea7\u54c1\u7ecf\u7406"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":148,""realname"":""xuyajing 28530""},{""id"":174720,""root"":12,""type"":""execution"",""account"":""x29245"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-08-09"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":267,""realname"":""xiaoyalun 29245""},{""id"":174721,""root"":12,""type"":""execution"",""account"":""y24914"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-05-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":6,""realname"":""yudong 24914""},{""id"":174722,""root"":12,""type"":""execution"",""account"":""y26925"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-10-12"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":55,""realname"":""yangzhiyu 26925""},{""id"":174723,""root"":12,""type"":""execution"",""account"":""y27441"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-01-07"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":60,""realname"":""yuanhaigan 27441""},{""id"":174724,""root"":12,""type"":""execution"",""account"":""y28116"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-07-07"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":123,""realname"":""yaowei 28116""},{""id"":174725,""root"":12,""type"":""execution"",""account"":""y28520"",""role"":""\u786c\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2022-11-02"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":254,""realname"":""yanghonghong 28520""},{""id"":174726,""root"":12,""type"":""execution"",""account"":""y29058"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-25"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":224,""realname"":""yangtianbao 29058""},{""id"":174727,""root"":12,""type"":""execution"",""account"":""y29292"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2022-07-25"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":232,""realname"":""yangwei 29292""},{""id"":174728,""root"":12,""type"":""execution"",""account"":""z25182"",""role"":""\u8f6f\u4ef6\u6d4b\u8bd5"",""position"":"""",""limited"":""no"",""join"":""2021-07-27"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":12,""realname"":""zhongmin 25182""},{""id"":174729,""root"":12,""type"":""execution"",""account"":""z27014"",""role"":""Windows\u5e94\u7528\u5f00\u53d1"",""position"":"""",""limited"":""no"",""join"":""2021-10-20"",""days"":0,""hours"":8,""estimate"":""0.00"",""consumed"":""0.00"",""left"":""0.00"",""order"":0,""totalHours"":0,""userID"":57,""realname"":""zhuowenpei 27014""}],""products"":[{""id"":272,""name"":""\u4e91\u5c4fWindows\u5e94\u7528"",""plans"":[]}],""caseReview"":false}",http://10.214.64.12/api.php/v1/executions/12?limit=100&page=1,null,2022-11-21 17:29:50 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv new file mode 100644 index 00000000000..c8b7dc49974 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv @@ -0,0 +1,3 @@ +id,params,data,url,input,created_at +4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""RD"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""feedback"":null,""acl"":""private"",""whitelist"":[],""reviewer"":""devlake,dev1"",""createdBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""createdDate"":""2022-11-17T06:42:25Z"",""createdVersion"":""17.6"",""order"":15,""vision"":""rnd"",""deleted"":""0"",""stories"":{""reviewing"":1,"""":0,""draft"":0,""active"":0,""closed"":0,""changing"":0},""plans"":1,""releases"":0,""builds"":0,""cases"":0,""projects"":0,""executions"":0,""bugs"":0,""docs"":0,""progress"":12.121,""caseReview"":false}",http://iwater.red:8000/api.php/v1/products/3?limit=100&page=1,null,2022-11-17 07:10:20.513 +5,"{""ConnectionId"":2,""ProductId"":2,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""RD"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""feedback"":null,""acl"":""private"",""whitelist"":[],""reviewer"":""devlake,dev1"",""createdBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""createdDate"":""2022-11-17T06:42:25Z"",""createdVersion"":""17.6"",""order"":15,""vision"":""rnd"",""deleted"":""0"",""stories"":{""reviewing"":1,"""":0,""draft"":0,""active"":0,""closed"":0,""changing"":0},""plans"":1,""releases"":0,""builds"":0,""cases"":0,""projects"":0,""executions"":0,""bugs"":0,""docs"":0,""progress"":12.121,""caseReview"":false}",http://iwater.red:8000/api.php/v1/products/2?limit=100&page=1,null,2022-11-17 07:10:20.513 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv new file mode 100644 index 00000000000..78fb04a5f60 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv @@ -0,0 +1,10 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":7,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":7,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5173\u4e8e\u6211\u4eec\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:24:19Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:24:19Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":null,""lastEditedDate"":null,""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":null,""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":6,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":6,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5408\u4f5c\u6d3d\u8c08\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:23:11Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:23:11Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":null,""lastEditedDate"":null,""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":null,""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +3,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":5,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":5,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u8bda\u8058\u82f1\u624d\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:21:39Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:21:39Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":null,""lastEditedDate"":null,""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":null,""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":4,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":4,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u552e\u540e\u670d\u52a1\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developed"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:20:16Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:20:16Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:42Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +5,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":3,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u6210\u679c\u5c55\u793a\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":0,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:18:10Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:18:10Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:38Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":2,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +6,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:16:37Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:16:37Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:33Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +7,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:09:49Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":null,""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:19Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +8,"{""ConnectionId"":2,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":8,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:16:37Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":""2012-06-05T02:16:37Z"",""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:33Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 +9,"{""ConnectionId"":3,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":9,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto"":[],""lib"":0,""fromStory"":0,""fromVersion"":1,""openedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""openedDate"":""2012-06-05T02:09:49Z"",""assignedTo"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""assignedDate"":null,""approvedDate"":""0000-00-00"",""lastEditedBy"":{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},""lastEditedDate"":""2012-06-05T02:25:19Z"",""changedBy"":"""",""changedDate"":""0000-00-00 00:00:00"",""reviewedBy"":null,""reviewedDate"":""2012-06-04T16:00:00Z"",""closedBy"":null,""closedDate"":null,""closedReason"":"""",""activatedDate"":""0000-00-00 00:00:00"",""toBug"":0,""childStories"":"""",""linkStories"":"""",""linkRequirements"":"""",""duplicateStory"":0,""version"":1,""storyChanged"":""0"",""feedbackBy"":"""",""notifyEmail"":"""",""URChanged"":""0"",""deleted"":false,""priOrder"":""1"",""planTitle"":""1.0\u7248\u672c "",""productStatus"":""normal""}",http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,null,2022-11-17 07:32:22.304 diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv new file mode 100644 index 00000000000..5b9361a6222 --- /dev/null +++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv @@ -0,0 +1,6 @@ +id,params,data,url,input,created_at +1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""}],""desc"":""\u4efb\u52a1\u63cf\u8ff0\u003Cspan\u003E \u003C\/span\u003E\u003Cbr \/\u003E\u003Cdiv\u003E\u003Cbr \/\u003E\u003C\/div\u003E"",""version"":1,""openedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""openedDate"":""2022-09-19T01:50:37Z"",""assignedTo"":{""id"":5,""account"":""dev2"",""avatar"":"""",""realname"":""\u5f00\u53d1\u4e59""},""assignedDate"":""2022-09-19T01:50:37Z"",""estStarted"":""2022-09-20"",""realStarted"":null,""finishedBy"":null,""finishedDate"":null,""finishedList"":"""",""canceledBy"":null,""canceledDate"":null,""closedBy"":null,""closedDate"":null,""planDuration"":0,""realDuration"":0,""closedReason"":"""",""lastEditedBy"":null,""lastEditedDate"":null,""activatedDate"":""0000-00-00 00:00:00"",""order"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""deleted"":false,""vision"":""rnd"",""storyID"":null,""storyTitle"":null,""product"":null,""branch"":null,""latestStoryVersion"":null,""storyStatus"":null,""assignedToRealName"":""\u5f00\u53d1\u4e59"",""priOrder"":""3"",""delay"":47,""needConfirm"":false,""progress"":21.11}",http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,null,2022-11-17 06:28:36.902 +2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""}],""desc"":""\u4efb\u52a1\u63cf\u8ff0\u003Cspan\u003E \u003C\/span\u003E\u003Cbr \/\u003E\u003Cdiv\u003E\u003Cbr \/\u003E\u003C\/div\u003E"",""version"":1,""openedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""openedDate"":""2022-09-19T01:50:37Z"",""assignedTo"":{""id"":5,""account"":""dev2"",""avatar"":"""",""realname"":""\u5f00\u53d1\u4e59""},""assignedDate"":""2022-09-19T01:50:37Z"",""estStarted"":""2022-09-20"",""realStarted"":null,""finishedBy"":null,""finishedDate"":null,""finishedList"":"""",""canceledBy"":null,""canceledDate"":null,""closedBy"":null,""closedDate"":null,""planDuration"":0,""realDuration"":0,""closedReason"":"""",""lastEditedBy"":null,""lastEditedDate"":null,""activatedDate"":""0000-00-00 00:00:00"",""order"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""deleted"":false,""vision"":""rnd"",""storyID"":null,""storyTitle"":null,""product"":null,""branch"":null,""latestStoryVersion"":null,""storyStatus"":null,""assignedToRealName"":""\u5f00\u53d1\u4e59"",""priOrder"":""3"",""delay"":47,""needConfirm"":false,""progress"":3}",http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,null,2022-11-17 06:48:56.248 +4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":-1,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""}],""desc"":""\u4efb\u52a1\u63cf\u8ff0\u003Cspan\u003E \u003C\/span\u003E\u003Cbr \/\u003E\u003Cdiv\u003E\u003Cbr \/\u003E\u003C\/div\u003E"",""version"":1,""openedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""openedDate"":""2022-09-19T01:50:37Z"",""assignedTo"":{""id"":5,""account"":""dev2"",""avatar"":"""",""realname"":""\u5f00\u53d1\u4e59""},""assignedDate"":""2022-09-19T01:50:37Z"",""estStarted"":""2022-09-20"",""realStarted"":null,""finishedBy"":null,""finishedDate"":null,""finishedList"":"""",""canceledBy"":null,""canceledDate"":null,""closedBy"":null,""closedDate"":null,""planDuration"":0,""realDuration"":0,""closedReason"":"""",""lastEditedBy"":null,""lastEditedDate"":null,""activatedDate"":""0000-00-00 00:00:00"",""order"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""deleted"":false,""vision"":""rnd"",""storyID"":null,""storyTitle"":null,""product"":null,""branch"":null,""latestStoryVersion"":null,""storyStatus"":null,""assignedToRealName"":""\u5f00\u53d1\u4e59"",""priOrder"":""3"",""delay"":47,""needConfirm"":false,""progress"":43.22121}",http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,null,2022-11-17 07:10:21.155 +5,"{""ConnectionId"":3,""ProductId"":1,""ExecutionId"":4,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""}],""desc"":""\u4efb\u52a1\u63cf\u8ff0\u003Cspan\u003E \u003C\/span\u003E\u003Cbr \/\u003E\u003Cdiv\u003E\u003Cbr \/\u003E\u003C\/div\u003E"",""version"":1,""openedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""openedDate"":""2022-09-19T01:50:37Z"",""assignedTo"":{""id"":5,""account"":""dev2"",""avatar"":"""",""realname"":""\u5f00\u53d1\u4e59""},""assignedDate"":""2022-09-19T01:50:37Z"",""estStarted"":""2022-09-20"",""realStarted"":null,""finishedBy"":null,""finishedDate"":null,""finishedList"":"""",""canceledBy"":null,""canceledDate"":null,""closedBy"":null,""closedDate"":null,""planDuration"":0,""realDuration"":0,""closedReason"":"""",""lastEditedBy"":null,""lastEditedDate"":null,""activatedDate"":""0000-00-00 00:00:00"",""order"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""deleted"":false,""vision"":""rnd"",""storyID"":null,""storyTitle"":null,""product"":null,""branch"":null,""latestStoryVersion"":null,""storyStatus"":null,""assignedToRealName"":""\u5f00\u53d1\u4e59"",""priOrder"":""3"",""delay"":47,""needConfirm"":false,""progress"":3}",http://iwater.red:8000/api.php/v1/executions/4/tasks?limit=100&page=1,null,2022-11-17 06:48:56.248 +6,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":3,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":-1,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"",""avatar"":"""",""realname"":""\u4ea7\u54c1\u7ecf\u7406""},{""id"":3,""account"":""projectManager"",""avatar"":"""",""realname"":""\u9879\u76ee\u7ecf\u7406""}],""desc"":""\u4efb\u52a1\u63cf\u8ff0\u003Cspan\u003E \u003C\/span\u003E\u003Cbr \/\u003E\u003Cdiv\u003E\u003Cbr \/\u003E\u003C\/div\u003E"",""version"":1,""openedBy"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""openedDate"":""2022-09-19T01:50:37Z"",""assignedTo"":{""id"":5,""account"":""dev2"",""avatar"":"""",""realname"":""\u5f00\u53d1\u4e59""},""assignedDate"":""2022-09-19T01:50:37Z"",""estStarted"":""2022-09-20"",""realStarted"":null,""finishedBy"":null,""finishedDate"":null,""finishedList"":"""",""canceledBy"":null,""canceledDate"":null,""closedBy"":null,""closedDate"":null,""planDuration"":0,""realDuration"":0,""closedReason"":"""",""lastEditedBy"":null,""lastEditedDate"":null,""activatedDate"":""0000-00-00 00:00:00"",""order"":0,""repo"":0,""mr"":0,""entry"":"""",""lines"":"""",""v1"":"""",""v2"":"""",""deleted"":false,""vision"":""rnd"",""storyID"":null,""storyTitle"":null,""product"":null,""branch"":null,""latestStoryVersion"":null,""storyStatus"":null,""assignedToRealName"":""\u5f00\u53d1\u4e59"",""priOrder"":""3"",""delay"":47,""needConfirm"":false,""progress"":43.22121}",http://iwater.red:8000/api.php/v1/executions/3/tasks?limit=100&page=1,null,2022-11-17 07:10:21.155 diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_accounts.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_accounts.csv new file mode 100644 index 00000000000..675868aa59f --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_accounts.csv @@ -0,0 +1 @@ +connection_id,id,account,avatar,realname,role,dept diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv new file mode 100644 index 00000000000..1f49d1a4c7f --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv @@ -0,0 +1,13 @@ +connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,feedback,result,repo,mr,entry,num_of_line,v1,v2,repo_type,issue_key,testtask,last_edited_by_id,last_edited_date,deleted,pri_order,severity_order,needconfirm,status_name,product_status +1,1,7,3,0,0,0,8,1,0,1,1,1,0,0,首页页面问题,,3,1,codeerror,,,,,"

[步骤]进入首页

+

[结果]出现乱码    

+

[期望]正常显示

",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal +1,2,7,3,0,0,0,9,1,1,2,1,15,0,0,新闻中心页面问题,hh,3,2,codeerror,",windows",",chrome",,,"

[步骤]进入新闻中心

+

[结果]页面出现乱码

+

[期望]正常显示rew

",delay,,,1,1,2022-10-05T04:16:44.000+00:00,,1114255335@qq.com,7,测试甲,2012-06-05T02:57:11.000+00:00,主干,0,,2022-10-05T04:19:22.000+00:00,2022-10-06,0,,,,0,,0,,0,0,0,0,,,,,,,1,1,2022-10-05T04:19:22.000+00:00,0,2,3,0,过期Bug,normal +1,3,7,3,0,0,0,10,1,0,3,2,6,0,0,成果展示页面问题,,3,1,codeerror,,,,,"

[步骤]进入成果展示    

+

[结果]乱码

+

[期望]正常显示

",active,,,0,0,,,,8,测试乙,2012-06-05T02:58:22.000+00:00,主干,4,开发甲,2012-06-05T02:58:22.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal +1,4,7,3,0,0,0,11,1,0,4,1,9,0,0,售后服务页面问题,,3,1,codeerror,,,,,"

[步骤]进入售后服务

+

[结果]乱码

+

[期望]正常显示

",resolved,,,1,0,,,,9,测试丙,2012-06-05T03:00:19.000+00:00,主干,9,测试丙,2022-10-05T04:10:08.000+00:00,,1,fixed,主干,2022-10-05T04:09:59.000+00:00,0,,0,,0,0,0,0,,,,,,,0,1,2022-10-05T04:10:08.000+00:00,0,1,3,0,已解决,normal diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_departments.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_departments.csv new file mode 100644 index 00000000000..01aca951f35 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_departments.csv @@ -0,0 +1 @@ +connection_id,id,name,parent,path,grade,order_in,position,dept_function,manager,manager_name diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv new file mode 100644 index 00000000000..10e80b83e40 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv @@ -0,0 +1,2 @@ +connection_id,id,project,model,type,lifetime,budget,budget_unit,attribute,percent,milestone,output,auth,parent,path,grade,name,code,plan_begin,plan_end,real_began,real_end,days,status,sub_status,pri,description,version,parent_version,plan_duration,real_duration,opened_by_id,opened_date,opened_version,last_edited_by_id,last_edited_date,closed_by_id,closed_date,canceled_by_id,canceled_date,suspended_date,po_id,pm_id,qd_id,rd_id,team,acl,order_in,vision,display_cards,fluid_board,deleted,total_hours,total_estimate,total_consumed,total_left,project_id,progress,case_review +1,1,7,,sprint,short,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2022-05-01T00:00:00.000+00:00,2022-06-01T00:00:00.000+00:00,,,0,doing,,1,开发企业网站的基本雏形。
,0,0,0,0,0,,,1,2022-11-21T06:00:56.000+00:00,0,,0,,,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,51.5,27.5,0,65.2,0 diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv new file mode 100644 index 00000000000..eff1a9f3345 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv @@ -0,0 +1,2 @@ +connection_id,id,program,name,code,bind,line,type,status,sub_status,description,po_id,qd_id,rd_id,acl,reviewer,created_by_id,created_date,created_version,order_in,deleted,plans,releases,builds,cases,projects,executions,bugs,docs,progress,case_review +1,3,10,产品名称1,产品代号2,0,31,normal,normal,,"产品描述1",1,1,1,private,"devlake,dev1",1,2022-11-17T06:42:25.000+00:00,17.6,15,0,1,0,0,0,0,0,0,0,12.121,0 diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv new file mode 100644 index 00000000000..2ece4f19af3 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv @@ -0,0 +1,8 @@ +connection_id,id,product,branch,version,order_in,vision,parent,module,plan,source,source_note,from_bug,feedback,title,keywords,type,category,pri,estimate,status,sub_status,color,stage,lib,from_story,from_version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,approved_date,last_edited_id,last_edited_date,changed_date,reviewed_by_id,reviewed_date,closed_id,closed_date,closed_reason,activated_date,to_bug,child_stories,link_stories,link_requirements,duplicate_story,story_changed,feedback_by,notify_email,ur_changed,deleted,pri_order,plan_title +1,1,3,0,1,0,rnd,0,1,1,po,,0,0,首页设计和开发,,story,feature,1,1,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:09:49.000+00:00,2,产品经理,,,2,2012-06-05T02:25:19.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,2,3,0,1,0,rnd,0,2,1,po,,0,0,新闻中心的设计和开发。,,story,feature,1,1,active,,,projected,0,0,1,2,产品经理,2012-06-05T02:16:37.000+00:00,2,产品经理,2012-06-05T02:16:37.000+00:00,,2,2012-06-05T02:25:33.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,3,3,0,2,0,rnd,0,3,1,po,,0,0,成果展示的设计和开发,,story,feature,1,0,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:18:10.000+00:00,2,产品经理,2012-06-05T02:18:10.000+00:00,,2,2012-06-05T02:25:38.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,4,3,0,1,0,rnd,0,4,1,po,,0,0,售后服务的设计和开发,,story,feature,1,1,active,,,developed,0,0,1,2,产品经理,2012-06-05T02:20:16.000+00:00,2,产品经理,2012-06-05T02:20:16.000+00:00,,2,2012-06-05T02:25:42.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,5,3,0,1,0,rnd,0,5,1,po,,0,0,诚聘英才的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:21:39.000+00:00,2,产品经理,2012-06-05T02:21:39.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,6,3,0,1,0,rnd,0,6,1,po,,0,0,合作洽谈的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:23:11.000+00:00,2,产品经理,2012-06-05T02:23:11.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本 +1,7,3,0,1,0,rnd,0,7,1,po,,0,0,关于我们的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:24:19.000+00:00,2,产品经理,2012-06-05T02:24:19.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本 diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv new file mode 100644 index 00000000000..50c89887399 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv @@ -0,0 +1,4 @@ +connection_id,id,execution_id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edited_date,activated_date,order_in,repo,mr,entry,num_of_line,v1,v2,deleted,vision,story_id,story_title,branch,latest_story_version,story_status,assigned_to_real_name,pri_order,need_confirm,progress +1,1,1,13,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述

,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11 +1,2,1,13,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,12.1,2.1,2022-10-01,wait,,,任务描述

,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3 +1,3,1,13,-1,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,11.2,0,2022-10-01,wait,,,任务描述

,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121 diff --git a/plugins/zentao/e2e/snapshot_tables/board_issues_bug.csv b/plugins/zentao/e2e/snapshot_tables/board_issues_bug.csv new file mode 100644 index 00000000000..af59f0f724d --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/board_issues_bug.csv @@ -0,0 +1,5 @@ +board_id,issue_id +zentao:ZentaoProduct:1:3,zentao:ZentaoBug:1:1 +zentao:ZentaoProduct:1:3,zentao:ZentaoBug:1:2 +zentao:ZentaoProduct:1:3,zentao:ZentaoBug:1:3 +zentao:ZentaoProduct:1:3,zentao:ZentaoBug:1:4 diff --git a/plugins/zentao/e2e/snapshot_tables/board_issues_story.csv b/plugins/zentao/e2e/snapshot_tables/board_issues_story.csv new file mode 100644 index 00000000000..2bfddefa4de --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/board_issues_story.csv @@ -0,0 +1,8 @@ +board_id,issue_id +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:1 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:2 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:3 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:4 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:5 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:6 +zentao:ZentaoProduct:1:3,zentao:ZentaoStory:1:7 diff --git a/plugins/zentao/e2e/snapshot_tables/board_issues_task.csv b/plugins/zentao/e2e/snapshot_tables/board_issues_task.csv new file mode 100644 index 00000000000..dbbf76bbdf3 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/board_issues_task.csv @@ -0,0 +1,4 @@ +board_id,issue_id +zentao:ZentaoProduct:1:3,zentao:ZentaoTask:1:1 +zentao:ZentaoProduct:1:3,zentao:ZentaoTask:1:2 +zentao:ZentaoProduct:1:3,zentao:ZentaoTask:1:3 diff --git a/plugins/zentao/e2e/snapshot_tables/boards_execution.csv b/plugins/zentao/e2e/snapshot_tables/boards_execution.csv new file mode 100644 index 00000000000..e596c62f986 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/boards_execution.csv @@ -0,0 +1,2 @@ +id,name,description,url,created_date,type +zentao:ZentaoExecution:1:1,企业网站第一期,开发企业网站的基本雏形。
,",7,1,",,sprint diff --git a/plugins/zentao/e2e/snapshot_tables/boards_product.csv b/plugins/zentao/e2e/snapshot_tables/boards_product.csv new file mode 100644 index 00000000000..1da72455adc --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/boards_product.csv @@ -0,0 +1,2 @@ +id,name,description,url,created_date,type +zentao:ZentaoProduct:1:3,产品名称1,"产品描述1",,2022-11-17T06:42:25.000+00:00,normal diff --git a/plugins/zentao/e2e/snapshot_tables/issues_bug.csv b/plugins/zentao/e2e/snapshot_tables/issues_bug.csv new file mode 100644 index 00000000000..9a3babe2ad2 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/issues_bug.csv @@ -0,0 +1,5 @@ +id,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component,deployment_id +zentao:ZentaoBug:1:1,,,1,首页页面问题,,,BUG,IN_PROGRESS,active,0,,2012-06-05T02:56:11.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:1,,0,0,0,7,测试甲,4,开发甲,,, +zentao:ZentaoBug:1:2,,,2,新闻中心页面问题,,,BUG,IN_PROGRESS,delay,0,,2012-06-05T02:57:11.000+00:00,2022-10-05T04:19:22.000+00:00,0,zentao:ZentaoStory:1:2,,0,0,0,7,测试甲,0,,,, +zentao:ZentaoBug:1:3,,,3,成果展示页面问题,,,BUG,IN_PROGRESS,active,0,,2012-06-05T02:58:22.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:3,,0,0,0,8,测试乙,4,开发甲,,, +zentao:ZentaoBug:1:4,,,4,售后服务页面问题,,,BUG,DONE,resolved,0,,2012-06-05T03:00:19.000+00:00,2022-10-05T04:10:08.000+00:00,0,zentao:ZentaoStory:1:4,,0,0,0,9,测试丙,9,测试丙,,, diff --git a/plugins/zentao/e2e/snapshot_tables/issues_story.csv b/plugins/zentao/e2e/snapshot_tables/issues_story.csv new file mode 100644 index 00000000000..e480b42f613 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/issues_story.csv @@ -0,0 +1,8 @@ +id,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component,deployment_id +zentao:ZentaoStory:1:1,,,1,首页设计和开发,,,REQUIREMENT,IN_PROGRESS,developing,0,,2012-06-05T02:09:49.000+00:00,2012-06-05T02:25:19.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:2,,,2,新闻中心的设计和开发。,,,REQUIREMENT,IN_PROGRESS,projected,0,,2012-06-05T02:16:37.000+00:00,2012-06-05T02:25:33.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:3,,,3,成果展示的设计和开发,,,REQUIREMENT,IN_PROGRESS,developing,0,,2012-06-05T02:18:10.000+00:00,2012-06-05T02:25:38.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:4,,,4,售后服务的设计和开发,,,REQUIREMENT,IN_PROGRESS,developed,0,,2012-06-05T02:20:16.000+00:00,2012-06-05T02:25:42.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:5,,,5,诚聘英才的设计和开发,,,REQUIREMENT,IN_PROGRESS,planned,0,,2012-06-05T02:21:39.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:6,,,6,合作洽谈的设计和开发,,,REQUIREMENT,IN_PROGRESS,planned,0,,2012-06-05T02:23:11.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, +zentao:ZentaoStory:1:7,,,7,关于我们的设计和开发,,,REQUIREMENT,IN_PROGRESS,planned,0,,2012-06-05T02:24:19.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,, diff --git a/plugins/zentao/e2e/snapshot_tables/issues_task.csv b/plugins/zentao/e2e/snapshot_tables/issues_task.csv new file mode 100644 index 00000000000..96505fad27f --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/issues_task.csv @@ -0,0 +1,4 @@ +id,url,icon_url,issue_key,title,description,epic_key,type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component,deployment_id +zentao:ZentaoTask:1:1,,,1,任务名称,任务描述

,,TASK,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,, +zentao:ZentaoTask:1:2,,,2,任务名称,任务描述

,,TASK,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,, +zentao:ZentaoTask:1:3,,,3,任务名称,任务描述

,,TASK,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:-1,,0,0,0,1,devlake,5,开发乙,,, diff --git a/plugins/zentao/e2e/snapshot_tables/teams.csv b/plugins/zentao/e2e/snapshot_tables/teams.csv new file mode 100644 index 00000000000..356c60dd4a8 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/teams.csv @@ -0,0 +1 @@ +id,name,alias,parent_id,sorting_index diff --git a/plugins/zentao/e2e/snapshot_tables/users.csv b/plugins/zentao/e2e/snapshot_tables/users.csv new file mode 100644 index 00000000000..605226ab789 --- /dev/null +++ b/plugins/zentao/e2e/snapshot_tables/users.csv @@ -0,0 +1 @@ +id,email,full_name,user_name,avatar_url,organization,created_date,status diff --git a/plugins/zentao/e2e/story_test.go b/plugins/zentao/e2e/story_test.go new file mode 100644 index 00000000000..c4bdf01d8ab --- /dev/null +++ b/plugins/zentao/e2e/story_test.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoStoryDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_stories.csv", + "_raw_zentao_api_stories") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoStory{}) + dataflowTester.Subtask(tasks.ExtractStoryMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoStory{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_stories.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + // verify conversion + dataflowTester.FlushTabler(&ticket.Issue{}) + dataflowTester.FlushTabler(&ticket.BoardIssue{}) + dataflowTester.Subtask(tasks.ConvertStoryMeta, taskData) + dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/issues_story.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + dataflowTester.VerifyTableWithOptions(&ticket.BoardIssue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/board_issues_story.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/e2e/task_test.go b/plugins/zentao/e2e/task_test.go new file mode 100644 index 00000000000..35563525907 --- /dev/null +++ b/plugins/zentao/e2e/task_test.go @@ -0,0 +1,68 @@ +/* +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. +*/ + +package e2e + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "testing" + + "github.com/apache/incubator-devlake/helpers/e2ehelper" + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" +) + +func TestZentaoTaskDataFlow(t *testing.T) { + + var zentao impl.Zentao + dataflowTester := e2ehelper.NewDataFlowTester(t, "zentao", zentao) + + taskData := &tasks.ZentaoTaskData{ + Options: &tasks.ZentaoOptions{ + ConnectionId: 1, + ProjectId: 1, + ProductId: 3, + ExecutionId: 1, + }, + } + + // import raw data table + dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_tasks.csv", + "_raw_zentao_api_tasks") + + // verify extraction + dataflowTester.FlushTabler(&models.ZentaoTask{}) + dataflowTester.Subtask(tasks.ExtractTaskMeta, taskData) + dataflowTester.VerifyTableWithOptions(&models.ZentaoTask{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/_tool_zentao_tasks.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + + dataflowTester.FlushTabler(&ticket.Issue{}) + dataflowTester.FlushTabler(&ticket.BoardIssue{}) + dataflowTester.Subtask(tasks.ConvertTaskMeta, taskData) + dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/issues_task.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) + dataflowTester.VerifyTableWithOptions(&ticket.BoardIssue{}, e2ehelper.TableOptions{ + CSVRelPath: "./snapshot_tables/board_issues_task.csv", + IgnoreTypes: []interface{}{common.NoPKModel{}}, + }) +} diff --git a/plugins/zentao/impl/impl.go b/plugins/zentao/impl/impl.go new file mode 100644 index 00000000000..8b10c1e9edf --- /dev/null +++ b/plugins/zentao/impl/impl.go @@ -0,0 +1,142 @@ +/* +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. +*/ + +package impl + +import ( + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/api" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "github.com/apache/incubator-devlake/plugins/zentao/models/migrationscripts" + "github.com/apache/incubator-devlake/plugins/zentao/tasks" + "github.com/spf13/viper" + "gorm.io/gorm" +) + +// make sure interface is implemented +var _ core.PluginMeta = (*Zentao)(nil) +var _ core.PluginInit = (*Zentao)(nil) +var _ core.PluginTask = (*Zentao)(nil) +var _ core.PluginApi = (*Zentao)(nil) +var _ core.PluginBlueprintV100 = (*Zentao)(nil) +var _ core.CloseablePluginTask = (*Zentao)(nil) + +type Zentao struct{} + +func (plugin Zentao) Description() string { + return "collect some Zentao data" +} + +func (plugin Zentao) Init(config *viper.Viper, logger core.Logger, db *gorm.DB) errors.Error { + api.Init(config, logger, db) + return nil +} + +func (plugin Zentao) SubTaskMetas() []core.SubTaskMeta { + // TODO add your sub task here + return []core.SubTaskMeta{ + tasks.CollectProductMeta, + tasks.ExtractProductMeta, + tasks.ConvertProductMeta, + tasks.CollectExecutionMeta, + tasks.ExtractExecutionMeta, + tasks.ConvertExecutionMeta, + tasks.CollectStoryMeta, + tasks.ExtractStoryMeta, + tasks.ConvertStoryMeta, + tasks.CollectBugMeta, + tasks.ExtractBugMeta, + tasks.ConvertBugMeta, + tasks.CollectTaskMeta, + tasks.ExtractTaskMeta, + tasks.ConvertTaskMeta, + tasks.CollectAccountMeta, + tasks.ExtractAccountMeta, + tasks.ConvertAccountMeta, + tasks.CollectDepartmentMeta, + tasks.ExtractDepartmentMeta, + tasks.ConvertDepartmentMeta, + } +} + +func (plugin Zentao) PrepareTaskData(taskCtx core.TaskContext, options map[string]interface{}) (interface{}, errors.Error) { + op, err := tasks.DecodeAndValidateTaskOptions(options) + if err != nil { + return nil, errors.Default.Wrap(err, "could not decode Zentao options") + } + connectionHelper := helper.NewConnectionHelper( + taskCtx, + nil, + ) + connection := &models.ZentaoConnection{} + err = connectionHelper.FirstById(connection, op.ConnectionId) + if err != nil { + return nil, errors.Default.Wrap(err, "unable to get Zentao connection by the given connection ID: %v") + } + + apiClient, err := tasks.NewZentaoApiClient(taskCtx, connection) + if err != nil { + return nil, errors.Default.Wrap(err, "unable to get Zentao API client instance: %v") + } + + return &tasks.ZentaoTaskData{ + Options: op, + ApiClient: apiClient, + }, nil +} + +// PkgPath information lost when compiled as plugin(.so) +func (plugin Zentao) RootPkgPath() string { + return "github.com/apache/incubator-devlake/plugins/zentao" +} + +func (plugin Zentao) MigrationScripts() []core.MigrationScript { + return migrationscripts.All() +} + +func (plugin Zentao) ApiResources() map[string]map[string]core.ApiResourceHandler { + return map[string]map[string]core.ApiResourceHandler{ + "test": { + "POST": api.TestConnection, + }, + "connections": { + "POST": api.PostConnections, + "GET": api.ListConnections, + }, + "connections/:connectionId": { + "GET": api.GetConnection, + "PATCH": api.PatchConnection, + "DELETE": api.DeleteConnection, + }, + } +} + +func (plugin Zentao) MakePipelinePlan(connectionId uint64, scope []*core.BlueprintScopeV100) (core.PipelinePlan, errors.Error) { + return api.MakePipelinePlan(plugin.SubTaskMetas(), connectionId, scope) +} + +func (plugin Zentao) Close(taskCtx core.TaskContext) errors.Error { + data, ok := taskCtx.GetData().(*tasks.ZentaoTaskData) + if !ok { + return errors.Default.New(fmt.Sprintf("GetData failed when try to close %+v", taskCtx)) + } + data.ApiClient.Release() + return nil +} diff --git a/plugins/zentao/models/access_token.go b/plugins/zentao/models/access_token.go new file mode 100644 index 00000000000..60bf2617878 --- /dev/null +++ b/plugins/zentao/models/access_token.go @@ -0,0 +1,27 @@ +/* +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. +*/ + +package models + +type ApiAccessTokenRequest struct { + Account string `json:"account"` + Password string `json:"password"` +} + +type ApiAccessTokenResponse struct { + Token string `json:"token"` +} diff --git a/plugins/zentao/models/account.go b/plugins/zentao/models/account.go new file mode 100644 index 00000000000..07b94c3073e --- /dev/null +++ b/plugins/zentao/models/account.go @@ -0,0 +1,37 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" +) + +type ZentaoAccount struct { + common.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Account string `json:"account" gorm:"type:varchar(100);index"` + Avatar string `json:"avatar" gorm:"type:varchar(255)"` + Realname string `json:"realname" gorm:"type:varchar(100);index"` + Role string `json:"role" gorm:"type:varchar(100);index"` + Dept int64 `json:"dept" gorm:"type:BIGINT NOT NULL;index"` +} + +func (ZentaoAccount) TableName() string { + return "_tool_zentao_accounts" +} diff --git a/plugins/zentao/models/archived/account.go b/plugins/zentao/models/archived/account.go new file mode 100644 index 00000000000..eb7a81d88f6 --- /dev/null +++ b/plugins/zentao/models/archived/account.go @@ -0,0 +1,37 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" +) + +type ZentaoAccount struct { + archived.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Account string `json:"account" gorm:"type:varchar(100);index"` + Avatar string `json:"avatar" gorm:"type:varchar(255)"` + Realname string `json:"realname" gorm:"type:varchar(100);index"` + Role string `json:"role" gorm:"type:varchar(100);index"` + Dept int64 `json:"dept" gorm:"type:BIGINT NOT NULL;index"` +} + +func (ZentaoAccount) TableName() string { + return "_tool_zentao_accounts" +} diff --git a/plugins/zentao/models/archived/bug.go b/plugins/zentao/models/archived/bug.go new file mode 100644 index 00000000000..373fabafb4c --- /dev/null +++ b/plugins/zentao/models/archived/bug.go @@ -0,0 +1,99 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoBug struct { + archived.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Product int64 `json:"product"` + Injection int `json:"injection"` + Identify int `json:"identify"` + Branch int `json:"branch"` + Module int `json:"module"` + Execution int64 `json:"execution"` + Plan int `json:"plan"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + Task int `json:"task"` + ToTask int `json:"toTask"` + ToStory int64 `json:"toStory"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Severity int `json:"severity"` + Pri int `json:"pri"` + Type string `json:"type"` + Os string `json:"os"` + Browser string `json:"browser"` + Hardware string `json:"hardware"` + Found string `json:"found"` + Steps string `json:"steps"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Confirmed int `json:"confirmed"` + ActivatedCount int `json:"activatedCount"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + FeedbackBy string `json:"feedbackBy"` + NotifyEmail string `json:"notifyEmail"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedBuild string `json:"openedBuild"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + Deadline string `json:"deadline"` + ResolvedById int64 + Resolution string `json:"resolution"` + ResolvedBuild string `json:"resolvedBuild"` + ResolvedDate *helper.Iso8601Time `json:"resolvedDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + DuplicateBug int `json:"duplicateBug"` + LinkBug string `json:"linkBug"` + Feedback int `json:"feedback"` + Result int `json:"result"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + RepoType string `json:"repoType"` + IssueKey string `json:"issueKey"` + Testtask int `json:"testtask"` + LastEditedById int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + SeverityOrder int `json:"severityOrder"` + Needconfirm bool `json:"needconfirm"` + StatusName string `json:"statusName"` + ProductStatus string `json:"productStatus"` +} + +func (ZentaoBug) TableName() string { + return "_tool_zentao_bugs" +} diff --git a/plugins/zentao/models/archived/connection.go b/plugins/zentao/models/archived/connection.go new file mode 100644 index 00000000000..d4cb2462d3b --- /dev/null +++ b/plugins/zentao/models/archived/connection.go @@ -0,0 +1,70 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" +) + +// TODO Please modify the following code to fit your needs +// This object conforms to what the frontend currently sends. +type ZentaoConnection struct { + RestConnection `mapstructure:",squash"` + //TODO you may need to use helper.BasicAuth instead of helper.AccessToken + BasicAuth `mapstructure:",squash"` +} + +type TestConnectionRequest struct { + Endpoint string `json:"endpoint"` + Proxy string `json:"proxy"` + BasicAuth `mapstructure:",squash"` +} + +// This object conforms to what the frontend currently expects. +type ZentaoResponse struct { + Name string `json:"name"` + ID int64 `json:"id"` + ZentaoConnection +} + +// Using User because it requires authentication. +type ApiUserResponse struct { + Id int64 + Name string `json:"name"` +} + +func (ZentaoConnection) TableName() string { + return "_tool_zentao_connections" +} + +type BasicAuth struct { + Username string `mapstructure:"username" validate:"required" json:"username"` + Password string `mapstructure:"password" validate:"required" json:"password"` +} + +type RestConnection struct { + BaseConnection `mapstructure:",squash"` + Endpoint string `mapstructure:"endpoint" validate:"required" json:"endpoint"` + Proxy string `mapstructure:"proxy" json:"proxy"` + RateLimitPerHour int `comment:"api request rate limt per hour" json:"rateLimit"` +} + +type BaseConnection struct { + Name string `gorm:"type:varchar(100);uniqueIndex" json:"name" validate:"required"` + archived.Model +} diff --git a/plugins/zentao/models/archived/department.go b/plugins/zentao/models/archived/department.go new file mode 100644 index 00000000000..08738aaaa67 --- /dev/null +++ b/plugins/zentao/models/archived/department.go @@ -0,0 +1,39 @@ +/* +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. +*/ + +package archived + +import "github.com/apache/incubator-devlake/models/migrationscripts/archived" + +type ZentaoDepartment struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Name string `json:"name" gorm:"type:varchar(100);index"` + Parent int `json:"parent" gorm:"type:varchar(100)"` + Path string `json:"path" gorm:"type:varchar(100)"` + Grade int `json:"grade"` + OrderIn int `json:"order"` + Position string `json:"position" gorm:"type:varchar(100)"` + DeptFunction string `json:"function" gorm:"type:varchar(100)"` + Manager string `json:"manager" gorm:"type:varchar(100)"` + ManagerName string `json:"managerName" gorm:"type:varchar(100)"` + archived.NoPKModel +} + +func (ZentaoDepartment) TableName() string { + return "_tool_zentao_departments" +} diff --git a/plugins/zentao/models/archived/execution.go b/plugins/zentao/models/archived/execution.go new file mode 100644 index 00000000000..669b38e6a75 --- /dev/null +++ b/plugins/zentao/models/archived/execution.go @@ -0,0 +1,89 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoExecution struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + Id int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + OpenedById int64 + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedById int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledById int64 + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + POId int64 + PMId int64 + QDId int64 + RDId int64 + Team string `json:"team"` + Acl string `json:"acl"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted bool `json:"deleted"` + TotalHours float64 `json:"totalHours"` + TotalEstimate float64 `json:"totalEstimate"` + TotalConsumed float64 `json:"totalConsumed"` + TotalLeft float64 `json:"totalLeft"` + ProjectId int64 + Progress float64 `json:"progress"` + CaseReview bool `json:"caseReview"` + archived.NoPKModel +} + +func (ZentaoExecution) TableName() string { + return "_tool_zentao_executions" +} diff --git a/plugins/zentao/models/archived/product.go b/plugins/zentao/models/archived/product.go new file mode 100644 index 00000000000..0a1e19cc5db --- /dev/null +++ b/plugins/zentao/models/archived/product.go @@ -0,0 +1,62 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoProduct struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + Id int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Program int `json:"program"` + Name string `json:"name"` + Code string `json:"code"` + Bind string `json:"bind"` + Line int `json:"line"` + Type string `json:"type"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Description string `json:"desc"` + POId int64 + QDId int64 + RDId int64 + Acl string `json:"acl"` + Reviewer string `json:"reviewer"` + CreatedById int64 + CreatedDate *helper.Iso8601Time `json:"createdDate"` + CreatedVersion string `json:"createdVersion"` + OrderIn int `json:"order"` + Deleted string `json:"deleted"` + Plans int `json:"plans"` + Releases int `json:"releases"` + Builds int `json:"builds"` + Cases int `json:"cases"` + Projects int `json:"projects"` + Executions int `json:"executions"` + Bugs int `json:"bugs"` + Docs int `json:"docs"` + Progress float64 `json:"progress"` + CaseReview bool `json:"caseReview"` + archived.NoPKModel +} + +func (ZentaoProduct) TableName() string { + return "_tool_zentao_products" +} diff --git a/plugins/zentao/models/archived/project.go b/plugins/zentao/models/archived/project.go new file mode 100644 index 00000000000..26ca01df2cc --- /dev/null +++ b/plugins/zentao/models/archived/project.go @@ -0,0 +1,113 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoProject struct { + archived.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Days int `json:"days"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + //OpenedBy string `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedBy string `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedBy string `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledBy string `json:"canceledBy"` + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + PO string `json:"PO"` + PM `json:"PM"` + QD string `json:"QD"` + RD string `json:"RD"` + Team string `json:"team"` + Acl string `json:"acl"` + Whitelist `json:"whitelist" gorm:"-"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted bool `json:"deleted"` + Delay int `json:"delay"` + Hours `json:"hours"` + TeamCount int `json:"teamCount"` + LeftTasks string `json:"leftTasks"` + //TeamMembers []interface{} `json:"teamMembers" gorm:"-"` + TotalEstimate float64 `json:"totalEstimate"` + TotalConsumed float64 `json:"totalConsumed"` + TotalLeft float64 `json:"totalLeft"` + Progress float64 `json:"progress"` + TotalReal int `json:"totalReal"` +} +type PM struct { + PmId int64 `json:"id"` + PmAccount string `json:"account"` + PmAvatar string `json:"avatar"` + PmRealname string `json:"realname"` +} +type Whitelist []struct { + WhitelistID int64 `json:"id"` + WhitelistAccount string `json:"account"` + WhitelistAvatar string `json:"avatar"` + WhitelistRealname string `json:"realname"` +} +type Hours struct { + HoursTotalEstimate float64 `json:"totalEstimate"` + HoursTotalConsumed float64 `json:"totalConsumed"` + HoursTotalLeft float64 `json:"totalLeft"` + HoursProgress float64 `json:"progress"` + HoursTotalReal int `json:"totalReal"` +} + +func (ZentaoProject) TableName() string { + return "_tool_zentao_projects" +} diff --git a/plugins/zentao/models/archived/story.go b/plugins/zentao/models/archived/story.go new file mode 100644 index 00000000000..6a2b43bd72a --- /dev/null +++ b/plugins/zentao/models/archived/story.go @@ -0,0 +1,89 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoStory struct { + archived.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Project int64 `json:"project"` + Product int64 `json:"product"` + Branch int `json:"branch"` + Version int `json:"version"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + Parent int64 `json:"parent"` + Module int `json:"module"` + Plan string `json:"plan"` + Source string `json:"source"` + SourceNote string `json:"sourceNote"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + FeedbackBy string `json:"feedbackBy"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Type string `json:"type"` + Category string `json:"category"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Stage string `json:"stage"` + StagedById int64 `json:"stagedBy"` + //Mailto []interface{} `json:"mailto"` + Lib int `json:"lib"` + FromStory int64 `json:"fromStory"` + FromVersion int `json:"fromVersion"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + ApprovedDate *helper.Iso8601Time `json:"approvedDate"` + LastEditedId int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ChangedDate *helper.Iso8601Time `json:"changedDate"` + ReviewedById int64 `json:"reviewedBy"` + ReviewedDate *helper.Iso8601Time `json:"reviewedDate"` + ClosedId int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + ClosedReason string `json:"closedReason"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + ToBug int `json:"toBug"` + ChildStories string `json:"childStories"` + LinkStories string `json:"linkStories"` + LinkRequirements string `json:"linkRequirements"` + DuplicateStory int64 `json:"duplicateStory"` + StoryChanged string `json:"storyChanged"` + NotifyEmail string `json:"notifyEmail"` + URChanged string `json:"URChanged"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + PlanTitle string `json:"planTitle"` +} + +func (ZentaoStory) TableName() string { + return "_tool_zentao_stories" +} diff --git a/plugins/zentao/models/archived/task.go b/plugins/zentao/models/archived/task.go new file mode 100644 index 00000000000..62c9db05ff1 --- /dev/null +++ b/plugins/zentao/models/archived/task.go @@ -0,0 +1,97 @@ +/* +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. +*/ + +package archived + +import ( + "github.com/apache/incubator-devlake/models/migrationscripts/archived" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoTask struct { + archived.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ExecutionId int64 `json:"execution_id"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Parent int64 `json:"parent"` + Execution int64 `json:"execution"` + Module int `json:"module"` + Design int `json:"design"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + DesignVersion int `json:"designVersion"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + FromIssue int `json:"fromIssue"` + Name string `json:"name"` + Type string `json:"type"` + Mode string `json:"mode"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Consumed float64 `json:"consumed"` + Deadline string `json:"deadline"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + //Mailto interface{} `json:"mailto"` + Description string `json:"desc"` + Version int `json:"version"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + EstStarted string `json:"estStarted"` + RealStarted *helper.Iso8601Time `json:"realStarted"` + FinishedId int64 + FinishedDate *helper.Iso8601Time `json:"finishedDate"` + FinishedList string `json:"finishedList"` + CanceledId int64 + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + ClosedReason string `json:"closedReason"` + LastEditedId int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + OrderIn int `json:"order"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + Deleted bool `json:"deleted"` + Vision string `json:"vision"` + StoryID int64 `json:"storyID"` + StoryTitle string `json:"storyTitle"` + Branch int `json:"branch"` + LatestStoryVersion int `json:"latestStoryVersion"` + StoryStatus string `json:"storyStatus"` + AssignedToRealName string `json:"assignedToRealName"` + PriOrder string `json:"priOrder"` + NeedConfirm bool `json:"needConfirm"` + Progress float64 `json:"progress"` +} + +func (ZentaoTask) TableName() string { + return "_tool_zentao_tasks" +} diff --git a/plugins/zentao/models/bug.go b/plugins/zentao/models/bug.go new file mode 100644 index 00000000000..86273234c62 --- /dev/null +++ b/plugins/zentao/models/bug.go @@ -0,0 +1,167 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoBugRes struct { + ID int64 `json:"id"` + Project int64 `json:"project"` + Product int64 `json:"product"` + Injection int `json:"injection"` + Identify int `json:"identify"` + Branch int `json:"branch"` + Module int `json:"module"` + Execution int64 `json:"execution"` + Plan int `json:"plan"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + Task int `json:"task"` + ToTask int `json:"toTask"` + ToStory int64 `json:"toStory"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Severity int `json:"severity"` + Pri int `json:"pri"` + Type string `json:"type"` + Os string `json:"os"` + Browser string `json:"browser"` + Hardware string `json:"hardware"` + Found string `json:"found"` + Steps string `json:"steps"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Confirmed int `json:"confirmed"` + ActivatedCount int `json:"activatedCount"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + FeedbackBy string `json:"feedbackBy"` + NotifyEmail string `json:"notifyEmail"` + OpenedBy *ZentaoAccount `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedBuild string `json:"openedBuild"` + AssignedTo *ZentaoAccount `json:"assignedTo"` + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + Deadline string `json:"deadline"` + ResolvedBy *ZentaoAccount `json:"resolvedBy"` + Resolution string `json:"resolution"` + ResolvedBuild string `json:"resolvedBuild"` + ResolvedDate *helper.Iso8601Time `json:"resolvedDate"` + ClosedBy *ZentaoAccount `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + DuplicateBug int `json:"duplicateBug"` + LinkBug string `json:"linkBug"` + Feedback int `json:"feedback"` + Result int `json:"result"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + RepoType string `json:"repoType"` + IssueKey string `json:"issueKey"` + Testtask int `json:"testtask"` + LastEditedBy *ZentaoAccount `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + SeverityOrder int `json:"severityOrder"` + Needconfirm bool `json:"needconfirm"` + StatusName string `json:"statusName"` + ProductStatus string `json:"productStatus"` +} + +type ZentaoBug struct { + common.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Product int64 `json:"product"` + Injection int `json:"injection"` + Identify int `json:"identify"` + Branch int `json:"branch"` + Module int `json:"module"` + Execution int64 `json:"execution"` + Plan int `json:"plan"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + Task int `json:"task"` + ToTask int `json:"toTask"` + ToStory int64 `json:"toStory"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Severity int `json:"severity"` + Pri int `json:"pri"` + Type string `json:"type"` + Os string `json:"os"` + Browser string `json:"browser"` + Hardware string `json:"hardware"` + Found string `json:"found"` + Steps string `json:"steps"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Confirmed int `json:"confirmed"` + ActivatedCount int `json:"activatedCount"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + FeedbackBy string `json:"feedbackBy"` + NotifyEmail string `json:"notifyEmail"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedBuild string `json:"openedBuild"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + Deadline string `json:"deadline"` + ResolvedById int64 + Resolution string `json:"resolution"` + ResolvedBuild string `json:"resolvedBuild"` + ResolvedDate *helper.Iso8601Time `json:"resolvedDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + DuplicateBug int `json:"duplicateBug"` + LinkBug string `json:"linkBug"` + Feedback int `json:"feedback"` + Result int `json:"result"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + RepoType string `json:"repoType"` + IssueKey string `json:"issueKey"` + Testtask int `json:"testtask"` + LastEditedById int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + SeverityOrder int `json:"severityOrder"` + Needconfirm bool `json:"needconfirm"` + StatusName string `json:"statusName"` + ProductStatus string `json:"productStatus"` +} + +func (ZentaoBug) TableName() string { + return "_tool_zentao_bugs" +} diff --git a/plugins/zentao/models/connection.go b/plugins/zentao/models/connection.go new file mode 100644 index 00000000000..84f608a56d1 --- /dev/null +++ b/plugins/zentao/models/connection.go @@ -0,0 +1,51 @@ +/* +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. +*/ + +package models + +import "github.com/apache/incubator-devlake/plugins/helper" + +// TODO Please modify the following code to fit your needs +// This object conforms to what the frontend currently sends. +type ZentaoConnection struct { + helper.RestConnection `mapstructure:",squash"` + //TODO you may need to use helper.BasicAuth instead of helper.AccessToken + helper.BasicAuth `mapstructure:",squash"` +} + +type TestConnectionRequest struct { + Endpoint string `json:"endpoint"` + Proxy string `json:"proxy"` + helper.BasicAuth `mapstructure:",squash"` +} + +// This object conforms to what the frontend currently expects. +type ZentaoResponse struct { + Name string `json:"name"` + ID int64 `json:"id"` + ZentaoConnection +} + +// Using User because it requires authentication. +type ApiUserResponse struct { + Id int64 + Name string `json:"name"` +} + +func (ZentaoConnection) TableName() string { + return "_tool_zentao_connections" +} diff --git a/plugins/zentao/models/department.go b/plugins/zentao/models/department.go new file mode 100644 index 00000000000..08f63c1acab --- /dev/null +++ b/plugins/zentao/models/department.go @@ -0,0 +1,41 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" +) + +type ZentaoDepartment struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Name string `json:"name" gorm:"type:varchar(100);index"` + Parent int64 `json:"parent" gorm:"type:varchar(100)"` + Path string `json:"path" gorm:"type:varchar(100)"` + Grade int `json:"grade"` + OrderIn int `json:"order"` + Position string `json:"position" gorm:"type:varchar(100)"` + DeptFunction string `json:"function" gorm:"type:varchar(100)"` + Manager string `json:"manager" gorm:"type:varchar(100)"` + ManagerName string `json:"managerName" gorm:"type:varchar(100)"` + common.NoPKModel +} + +func (ZentaoDepartment) TableName() string { + return "_tool_zentao_departments" +} diff --git a/plugins/zentao/models/execution.go b/plugins/zentao/models/execution.go new file mode 100644 index 00000000000..337da46fb29 --- /dev/null +++ b/plugins/zentao/models/execution.go @@ -0,0 +1,225 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoExecutionRes struct { + ID int64 `json:"id"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + OpenedBy *ZentaoAccount `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedBy *ZentaoAccount `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedBy *ZentaoAccount `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledBy *ZentaoAccount `json:"canceledBy"` + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + PO *ZentaoAccount `json:"PO"` + PM *ZentaoAccount `json:"PM"` + QD *ZentaoAccount `json:"QD"` + RD *ZentaoAccount `json:"RD"` + Team string `json:"team"` + Acl string `json:"acl"` + Whitelist []*ZentaoAccount `json:"whitelist"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted bool `json:"deleted"` + TotalHours float64 `json:"totalHours"` + TotalEstimate float64 `json:"totalEstimate"` + TotalConsumed float64 `json:"totalConsumed"` + TotalLeft float64 `json:"totalLeft"` + ProjectInfo struct { + ID int64 `json:"id"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + OpenedBy string `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedBy string `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedBy string `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledBy string `json:"canceledBy"` + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + PO string `json:"PO"` + PM string `json:"PM"` + QD string `json:"QD"` + RD string `json:"RD"` + Team string `json:"team"` + Acl string `json:"acl"` + Whitelist string `json:"whitelist"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted string `json:"deleted"` + } `json:"projectInfo"` + Progress float64 `json:"progress"` + TeamMembers []struct { + ID int64 `json:"id"` + Root int `json:"root"` + Type string `json:"type"` + Account string `json:"account"` + Role string `json:"role"` + Position string `json:"position"` + Limited string `json:"limited"` + Join string `json:"join"` + Hours int `json:"hours"` + Estimate string `json:"estimate"` + Consumed string `json:"consumed"` + Left string `json:"left"` + OrderIn int `json:"order"` + TotalHours float64 `json:"totalHours"` + UserID int64 `json:"userID"` + Realname string `json:"realname"` + } `json:"teamMembers"` + Products []struct { + ID int64 `json:"id"` + Name string `json:"name"` + Plans []interface{} `json:"plans"` + } `json:"products"` + CaseReview bool `json:"caseReview"` +} + +type ZentaoExecution struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + Id int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + OpenedById int64 + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedById int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledById int64 + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + POId int64 + PMId int64 + QDId int64 + RDId int64 + Team string `json:"team"` + Acl string `json:"acl"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted bool `json:"deleted"` + TotalHours float64 `json:"totalHours"` + TotalEstimate float64 `json:"totalEstimate"` + TotalConsumed float64 `json:"totalConsumed"` + TotalLeft float64 `json:"totalLeft"` + ProjectId int64 + Progress float64 `json:"progress"` + CaseReview bool `json:"caseReview"` + common.NoPKModel +} + +func (ZentaoExecution) TableName() string { + return "_tool_zentao_executions" +} diff --git a/plugins/zentao/models/migrationscripts/20221121_add_init_tables.go b/plugins/zentao/models/migrationscripts/20221121_add_init_tables.go new file mode 100644 index 00000000000..732f960e89a --- /dev/null +++ b/plugins/zentao/models/migrationscripts/20221121_add_init_tables.go @@ -0,0 +1,68 @@ +/* +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. +*/ + +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/helpers/migrationhelper" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/zentao/models/archived" +) + +type addInitTables struct{} + +func (*addInitTables) Up(basicRes core.BasicRes) errors.Error { + db := basicRes.GetDal() + err := db.DropTables( + &archived.ZentaoConnection{}, + &archived.ZentaoProject{}, + &archived.ZentaoProduct{}, + &archived.ZentaoExecution{}, + &archived.ZentaoStory{}, + &archived.ZentaoBug{}, + &archived.ZentaoTask{}, + "_tool_zentao_bugs`", + "_tool_zentao_executions`", + "_tool_zentao_products`", + "_tool_zentao_stories`", + "_tool_zentao_tasks`", + ) + if err != nil { + return err + } + return migrationhelper.AutoMigrateTables( + basicRes, + &archived.ZentaoConnection{}, + &archived.ZentaoProject{}, + &archived.ZentaoProduct{}, + &archived.ZentaoExecution{}, + &archived.ZentaoStory{}, + &archived.ZentaoBug{}, + &archived.ZentaoTask{}, + &archived.ZentaoAccount{}, + &archived.ZentaoDepartment{}, + ) +} + +func (*addInitTables) Version() uint64 { + return 20221121000001 +} + +func (*addInitTables) Name() string { + return "zentao init schemas" +} diff --git a/plugins/zentao/models/migrationscripts/register.go b/plugins/zentao/models/migrationscripts/register.go new file mode 100644 index 00000000000..c0c766cd617 --- /dev/null +++ b/plugins/zentao/models/migrationscripts/register.go @@ -0,0 +1,29 @@ +/* +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. +*/ + +package migrationscripts + +import ( + "github.com/apache/incubator-devlake/plugins/core" +) + +// All return all the migration scripts +func All() []core.MigrationScript { + return []core.MigrationScript{ + new(addInitTables), + } +} diff --git a/plugins/zentao/models/product.go b/plugins/zentao/models/product.go new file mode 100644 index 00000000000..b9e895f8265 --- /dev/null +++ b/plugins/zentao/models/product.go @@ -0,0 +1,106 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoProductRes struct { + ID int64 `json:"id"` + Program int `json:"program"` + Name string `json:"name"` + Code string `json:"code"` + Bind string `json:"bind"` + Line int `json:"line"` + Type string `json:"type"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Description string `json:"desc"` + PO *ZentaoAccount `json:"PO"` + QD *ZentaoAccount `json:"QD"` + RD *ZentaoAccount `json:"RD"` + Feedback interface{} `json:"feedback"` + Acl string `json:"acl"` + Whitelist []interface{} `json:"whitelist"` + Reviewer string `json:"reviewer"` + CreatedBy *ZentaoAccount `json:"createdBy"` + CreatedDate *helper.Iso8601Time `json:"createdDate"` + CreatedVersion string `json:"createdVersion"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + Deleted string `json:"deleted"` + Stories struct { + Active int `json:"active"` + Reviewing int `json:"reviewing"` + int `json:""` + Draft int `json:"draft"` + Closed int `json:"closed"` + Changing int `json:"changing"` + } `json:"stories"` + Plans int `json:"plans"` + Releases int `json:"releases"` + Builds int `json:"builds"` + Cases int `json:"cases"` + Projects int `json:"projects"` + Executions int `json:"executions"` + Bugs int `json:"bugs"` + Docs int `json:"docs"` + Progress float64 `json:"progress"` + CaseReview bool `json:"caseReview"` +} + +type ZentaoProduct struct { + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + Id int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Program int `json:"program"` + Name string `json:"name"` + Code string `json:"code"` + Bind string `json:"bind"` + Line int `json:"line"` + Type string `json:"type"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Description string `json:"desc"` + POId int64 + QDId int64 + RDId int64 + Acl string `json:"acl"` + Reviewer string `json:"reviewer"` + CreatedById int64 + CreatedDate *helper.Iso8601Time `json:"createdDate"` + CreatedVersion string `json:"createdVersion"` + OrderIn int `json:"order"` + Deleted string `json:"deleted"` + Plans int `json:"plans"` + Releases int `json:"releases"` + Builds int `json:"builds"` + Cases int `json:"cases"` + Projects int `json:"projects"` + Executions int `json:"executions"` + Bugs int `json:"bugs"` + Docs int `json:"docs"` + Progress float64 `json:"progress"` + CaseReview bool `json:"caseReview"` + common.NoPKModel +} + +func (ZentaoProduct) TableName() string { + return "_tool_zentao_products" +} diff --git a/plugins/zentao/models/project.go b/plugins/zentao/models/project.go new file mode 100644 index 00000000000..c97130fe260 --- /dev/null +++ b/plugins/zentao/models/project.go @@ -0,0 +1,113 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoProject struct { + common.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Model string `json:"model"` + Type string `json:"type"` + Lifetime string `json:"lifetime"` + Budget string `json:"budget"` + BudgetUnit string `json:"budgetUnit"` + Attribute string `json:"attribute"` + Percent int `json:"percent"` + Milestone string `json:"milestone"` + Output string `json:"output"` + Auth string `json:"auth"` + Parent int64 `json:"parent"` + Path string `json:"path"` + Grade int `json:"grade"` + Name string `json:"name"` + Code string `json:"code"` + PlanBegin *helper.Iso8601Time `json:"begin"` + PlanEnd *helper.Iso8601Time `json:"end"` + RealBegan *helper.Iso8601Time `json:"realBegan"` + RealEnd *helper.Iso8601Time `json:"realEnd"` + Days int `json:"days"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Pri string `json:"pri"` + Description string `json:"desc"` + Version int `json:"version"` + ParentVersion int `json:"parentVersion"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + //OpenedBy string `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + OpenedVersion string `json:"openedVersion"` + LastEditedBy string `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ClosedBy string `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + CanceledBy string `json:"canceledBy"` + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + SuspendedDate *helper.Iso8601Time `json:"suspendedDate"` + PO string `json:"PO"` + PM `json:"PM"` + QD string `json:"QD"` + RD string `json:"RD"` + Team string `json:"team"` + Acl string `json:"acl"` + Whitelist `json:"whitelist" gorm:"-"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + DisplayCards int `json:"displayCards"` + FluidBoard string `json:"fluidBoard"` + Deleted bool `json:"deleted"` + Delay int `json:"delay"` + Hours `json:"hours"` + TeamCount int `json:"teamCount"` + LeftTasks string `json:"leftTasks"` + //TeamMembers []interface{} `json:"teamMembers" gorm:"-"` + TotalEstimate float64 `json:"totalEstimate"` + TotalConsumed float64 `json:"totalConsumed"` + TotalLeft float64 `json:"totalLeft"` + Progress float64 `json:"progress"` + TotalReal int `json:"totalReal"` +} +type PM struct { + PmId int64 `json:"id"` + PmAccount string `json:"account"` + PmAvatar string `json:"avatar"` + PmRealname string `json:"realname"` +} +type Whitelist []struct { + WhitelistID int64 `json:"id"` + WhitelistAccount string `json:"account"` + WhitelistAvatar string `json:"avatar"` + WhitelistRealname string `json:"realname"` +} +type Hours struct { + HoursTotalEstimate float64 `json:"totalEstimate"` + HoursTotalConsumed float64 `json:"totalConsumed"` + HoursTotalLeft float64 `json:"totalLeft"` + HoursProgress float64 `json:"progress"` + HoursTotalReal float64 `json:"totalReal"` +} + +func (ZentaoProject) TableName() string { + return "_tool_zentao_projects" +} diff --git a/plugins/zentao/models/story.go b/plugins/zentao/models/story.go new file mode 100644 index 00000000000..a737e50e143 --- /dev/null +++ b/plugins/zentao/models/story.go @@ -0,0 +1,144 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoStoryRes struct { + ID int64 `json:"id"` + Vision string `json:"vision"` + Parent int64 `json:"parent"` + Product int64 `json:"product"` + Branch int `json:"branch"` + Module int `json:"module"` + Plan string `json:"plan"` + Source string `json:"source"` + SourceNote string `json:"sourceNote"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Type string `json:"type"` + Category string `json:"category"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Stage string `json:"stage"` + Mailto []interface{} `json:"mailto"` + Lib int `json:"lib"` + FromStory int64 `json:"fromStory"` + FromVersion int `json:"fromVersion"` + OpenedBy *ZentaoAccount `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedTo *ZentaoAccount `json:"assignedTo"` + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + ApprovedDate *helper.Iso8601Time `json:"approvedDate"` + LastEditedBy *ZentaoAccount `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ChangedBy string `json:"changedBy"` + ChangedDate *helper.Iso8601Time `json:"changedDate"` + ReviewedBy *ZentaoAccount `json:"reviewedBy"` + ReviewedDate *helper.Iso8601Time `json:"reviewedDate"` + ClosedBy *ZentaoAccount `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + ClosedReason string `json:"closedReason"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + ToBug int `json:"toBug"` + ChildStories string `json:"childStories"` + LinkStories string `json:"linkStories"` + LinkRequirements string `json:"linkRequirements"` + DuplicateStory int64 `json:"duplicateStory"` + Version int `json:"version"` + StoryChanged string `json:"storyChanged"` + FeedbackBy string `json:"feedbackBy"` + NotifyEmail string `json:"notifyEmail"` + URChanged string `json:"URChanged"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + PlanTitle string `json:"planTitle"` + ProductStatus string `json:"productStatus"` +} + +type ZentaoStory struct { + common.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL" ` + Product int64 `json:"product"` + Branch int `json:"branch"` + Version int `json:"version"` + OrderIn int `json:"order"` + Vision string `json:"vision"` + Parent int64 `json:"parent"` + Module int `json:"module"` + Plan string `json:"plan"` + Source string `json:"source"` + SourceNote string `json:"sourceNote"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + Title string `json:"title"` + Keywords string `json:"keywords"` + Type string `json:"type"` + Category string `json:"category"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Stage string `json:"stage"` + //Mailto []interface{} `json:"mailto"` + Lib int `json:"lib"` + FromStory int64 `json:"fromStory"` + FromVersion int `json:"fromVersion"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + ApprovedDate *helper.Iso8601Time `json:"approvedDate"` + LastEditedId int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ChangedDate *helper.Iso8601Time `json:"changedDate"` + ReviewedById int64 `json:"reviewedBy"` + ReviewedDate *helper.Iso8601Time `json:"reviewedDate"` + ClosedId int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + ClosedReason string `json:"closedReason"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + ToBug int `json:"toBug"` + ChildStories string `json:"childStories"` + LinkStories string `json:"linkStories"` + LinkRequirements string `json:"linkRequirements"` + DuplicateStory int64 `json:"duplicateStory"` + StoryChanged string `json:"storyChanged"` + FeedbackBy string `json:"feedbackBy"` + NotifyEmail string `json:"notifyEmail"` + URChanged string `json:"URChanged"` + Deleted bool `json:"deleted"` + PriOrder string `json:"priOrder"` + PlanTitle string `json:"planTitle"` +} + +func (ZentaoStory) TableName() string { + return "_tool_zentao_stories" +} diff --git a/plugins/zentao/models/task.go b/plugins/zentao/models/task.go new file mode 100644 index 00000000000..47501a23933 --- /dev/null +++ b/plugins/zentao/models/task.go @@ -0,0 +1,166 @@ +/* +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. +*/ + +package models + +import ( + "github.com/apache/incubator-devlake/models/common" + "github.com/apache/incubator-devlake/plugins/helper" +) + +type ZentaoTaskRes struct { + Id int64 `json:"id"` + Project int64 `json:"project"` + Parent int64 `json:"parent"` + Execution int64 `json:"execution"` + Module int `json:"module"` + Design int `json:"design"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + DesignVersion int `json:"designVersion"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + FromIssue int `json:"fromIssue"` + Name string `json:"name"` + Type string `json:"type"` + Mode string `json:"mode"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Consumed float64 `json:"consumed"` + Deadline string `json:"deadline"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + Mailto []*ZentaoAccount `json:"mailto"` + Description string `json:"desc"` + Version int `json:"version"` + OpenedBy *ZentaoAccount `json:"openedBy"` + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedTo *ZentaoAccount `json:"assignedTo"` + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + EstStarted string `json:"estStarted"` + RealStarted *helper.Iso8601Time `json:"realStarted"` + FinishedBy *ZentaoAccount `json:"finishedBy"` + FinishedDate *helper.Iso8601Time `json:"finishedDate"` + FinishedList string `json:"finishedList"` + CanceledBy *ZentaoAccount `json:"canceledBy"` + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + ClosedBy *ZentaoAccount `json:"closedBy"` + ClosedDate *helper.Iso8601Time `json:"closedDate"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + ClosedReason string `json:"closedReason"` + LastEditedBy *ZentaoAccount `json:"lastEditedBy"` + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + OrderIn int `json:"order"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + Deleted bool `json:"deleted"` + Vision string `json:"vision"` + StoryID int64 `json:"storyID"` + StoryTitle string `json:"storyTitle"` + Branch interface { + } `json:"branch"` + LatestStoryVersion interface { + } `json:"latestStoryVersion"` + StoryStatus interface { + } `json:"storyStatus"` + AssignedToRealName string `json:"assignedToRealName"` + PriOrder string `json:"priOrder"` + Delay int `json:"delay"` + NeedConfirm bool `json:"needConfirm"` + Progress float64 `json:"progress"` +} + +type ZentaoTask struct { + common.NoPKModel + ConnectionId uint64 `gorm:"primaryKey;type:BIGINT NOT NULL"` + ExecutionId int64 `json:"execution_id"` + ID int64 `json:"id" gorm:"primaryKey;type:BIGINT NOT NULL"` + Project int64 `json:"project"` + Parent int64 `json:"parent"` + Execution int64 `json:"execution"` + Module int `json:"module"` + Design int `json:"design"` + Story int64 `json:"story"` + StoryVersion int `json:"storyVersion"` + DesignVersion int `json:"designVersion"` + FromBug int `json:"fromBug"` + Feedback int `json:"feedback"` + FromIssue int `json:"fromIssue"` + Name string `json:"name"` + Type string `json:"type"` + Mode string `json:"mode"` + Pri int `json:"pri"` + Estimate float64 `json:"estimate"` + Consumed float64 `json:"consumed"` + Deadline string `json:"deadline"` + Status string `json:"status"` + SubStatus string `json:"subStatus"` + Color string `json:"color"` + //Mailto interface{} `json:"mailto"` + Description string `json:"desc"` + Version int `json:"version"` + OpenedById int64 + OpenedByName string + OpenedDate *helper.Iso8601Time `json:"openedDate"` + AssignedToId int64 + AssignedToName string + AssignedDate *helper.Iso8601Time `json:"assignedDate"` + EstStarted string `json:"estStarted"` + RealStarted *helper.Iso8601Time `json:"realStarted"` + FinishedId int64 + FinishedDate *helper.Iso8601Time `json:"finishedDate"` + FinishedList string `json:"finishedList"` + CanceledId int64 + CanceledDate *helper.Iso8601Time `json:"canceledDate"` + ClosedById int64 + ClosedDate *helper.Iso8601Time `json:"closedDate"` + PlanDuration int `json:"planDuration"` + RealDuration int `json:"realDuration"` + ClosedReason string `json:"closedReason"` + LastEditedId int64 + LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"` + ActivatedDate *helper.Iso8601Time `json:"activatedDate"` + OrderIn int `json:"order"` + Repo int `json:"repo"` + Mr int `json:"mr"` + Entry string `json:"entry"` + NumOfLine string `json:"lines"` + V1 string `json:"v1"` + V2 string `json:"v2"` + Deleted bool `json:"deleted"` + Vision string `json:"vision"` + StoryID int64 `json:"storyID"` + StoryTitle string `json:"storyTitle"` + Branch int `json:"branch"` + LatestStoryVersion int `json:"latestStoryVersion"` + StoryStatus string `json:"storyStatus"` + AssignedToRealName string `json:"assignedToRealName"` + PriOrder string `json:"priOrder"` + NeedConfirm bool `json:"needConfirm"` + Progress float64 `json:"progress"` +} + +func (ZentaoTask) TableName() string { + return "_tool_zentao_tasks" +} diff --git a/plugins/zentao/tasks/account_collector.go b/plugins/zentao/tasks/account_collector.go new file mode 100644 index 00000000000..a7f66446246 --- /dev/null +++ b/plugins/zentao/tasks/account_collector.go @@ -0,0 +1,82 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_ACCOUNT_TABLE = "zentao_api_accounts" + +var _ core.SubTaskEntryPoint = CollectAccount + +func CollectAccount(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_ACCOUNT_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "/users", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Users []json.RawMessage `json:"users"` + } + err := helper.UnmarshalResponse(res, &data) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector") + } + return data.Users, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectAccountMeta = core.SubTaskMeta{ + Name: "CollectAccount", + EntryPoint: CollectAccount, + EnabledByDefault: true, + Description: "Collect Account data from Zentao api", +} diff --git a/plugins/zentao/tasks/account_convertor.go b/plugins/zentao/tasks/account_convertor.go new file mode 100644 index 00000000000..be17d1172da --- /dev/null +++ b/plugins/zentao/tasks/account_convertor.go @@ -0,0 +1,91 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/crossdomain" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" +) + +var _ core.SubTaskEntryPoint = ConvertAccount + +var ConvertAccountMeta = core.SubTaskMeta{ + Name: "convertAccount", + EntryPoint: ConvertAccount, + EnabledByDefault: true, + Description: "convert Zentao account", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertAccount(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + accountIdGen := didgen.NewDomainIdGenerator(&models.ZentaoAccount{}) + deptIdGen := didgen.NewDomainIdGenerator(&models.ZentaoDepartment{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoAccount{}), + dal.Where(`_tool_zentao_accounts.connection_id = ?`, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoAccount{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_ACCOUNT_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolEntity := inputRow.(*models.ZentaoAccount) + + domainEntity := &crossdomain.Account{ + DomainEntity: domainlayer.DomainEntity{ + Id: accountIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID), + }, + FullName: toolEntity.Realname, + UserName: toolEntity.Account, + AvatarUrl: toolEntity.Avatar, + Organization: deptIdGen.Generate(toolEntity.ConnectionId, toolEntity.Dept), + } + results := make([]interface{}, 0) + results = append(results, domainEntity) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/account_extractor.go b/plugins/zentao/tasks/account_extractor.go new file mode 100644 index 00000000000..f982d837e88 --- /dev/null +++ b/plugins/zentao/tasks/account_extractor.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractAccount + +var ExtractAccountMeta = core.SubTaskMeta{ + Name: "extractAccount", + EntryPoint: ExtractAccount, + EnabledByDefault: true, + Description: "extract Zentao account", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractAccount(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_ACCOUNT_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + account := &models.ZentaoAccount{} + err := json.Unmarshal(row.Data, account) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + account.ConnectionId = data.Options.ConnectionId + results := make([]interface{}, 0) + results = append(results, account) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/api_client.go b/plugins/zentao/tasks/api_client.go new file mode 100644 index 00000000000..aff7b4d69fe --- /dev/null +++ b/plugins/zentao/tasks/api_client.go @@ -0,0 +1,95 @@ +/* +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. +*/ + +package tasks + +import ( + "fmt" + "github.com/apache/incubator-devlake/errors" + "net/http" + "strconv" + "time" + + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +func NewZentaoApiClient(taskCtx core.TaskContext, connection *models.ZentaoConnection) (*helper.ApiAsyncClient, error) { + authApiClient, err := helper.NewApiClient(taskCtx.GetContext(), connection.Endpoint, nil, 0, connection.Proxy, taskCtx) + if err != nil { + return nil, err + } + + // request for access token + tokenReqBody := &models.ApiAccessTokenRequest{ + Account: connection.Username, + Password: connection.Password, + } + tokenRes, err := authApiClient.Post("/tokens", nil, tokenReqBody, nil) + if err != nil { + return nil, err + } + tokenResBody := &models.ApiAccessTokenResponse{} + err = helper.UnmarshalResponse(tokenRes, tokenResBody) + if err != nil { + return nil, err + } + if tokenResBody.Token == "" { + return nil, errors.Default.New("failed to request access token") + } + // real request apiClient + apiClient, err := helper.NewApiClient(taskCtx.GetContext(), connection.Endpoint, nil, 0, connection.Proxy, taskCtx) + if err != nil { + return nil, err + } + // set token + apiClient.SetHeaders(map[string]string{ + "Token": fmt.Sprintf("%v", tokenResBody.Token), + }) + + // create rate limit calculator + rateLimiter := &helper.ApiRateLimitCalculator{ + UserRateLimitPerHour: connection.RateLimitPerHour, + DynamicRateLimit: func(res *http.Response) (int, time.Duration, errors.Error) { + rateLimitHeader := res.Header.Get("RateLimit-Limit") + if rateLimitHeader == "" { + // use default + return 0, 0, nil + } + rateLimit, err := strconv.Atoi(rateLimitHeader) + if err != nil { + return 0, 0, errors.Default.Wrap(err, "failed to parse RateLimit-Limit header: %w") + } + // seems like {{ .plugin-ame }} rate limit is on minute basis + return rateLimit, 1 * time.Minute, nil + }, + } + asyncApiClient, err := helper.CreateAsyncApiClient( + taskCtx, + apiClient, + rateLimiter, + ) + if err != nil { + return nil, err + } + return asyncApiClient, nil +} + +type ZentaoPagination struct { + Page int `json:"page"` +} diff --git a/plugins/zentao/tasks/bug_collector.go b/plugins/zentao/tasks/bug_collector.go new file mode 100644 index 00000000000..84343858382 --- /dev/null +++ b/plugins/zentao/tasks/bug_collector.go @@ -0,0 +1,82 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_BUG_TABLE = "zentao_api_bugs" + +var _ core.SubTaskEntryPoint = CollectBug + +func CollectBug(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_BUG_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "/products/{{ .Params.ProductId }}/bugs", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Bugs []json.RawMessage `json:"bugs"` + } + err := helper.UnmarshalResponse(res, &data) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector") + } + return data.Bugs, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectBugMeta = core.SubTaskMeta{ + Name: "CollectBug", + EntryPoint: CollectBug, + EnabledByDefault: true, + Description: "Collect Bug data from Zentao api", +} diff --git a/plugins/zentao/tasks/bug_convertor.go b/plugins/zentao/tasks/bug_convertor.go new file mode 100644 index 00000000000..ed9735764fd --- /dev/null +++ b/plugins/zentao/tasks/bug_convertor.go @@ -0,0 +1,116 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" + "strconv" +) + +var _ core.SubTaskEntryPoint = ConvertBug + +var ConvertBugMeta = core.SubTaskMeta{ + Name: "convertBug", + EntryPoint: ConvertBug, + EnabledByDefault: true, + Description: "convert Zentao bug", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertBug(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + bugIdGen := didgen.NewDomainIdGenerator(&models.ZentaoBug{}) + boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{}) + storyIdGen := didgen.NewDomainIdGenerator(&models.ZentaoStory{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoBug{}), + dal.Where(`_tool_zentao_bugs.product = ? and + _tool_zentao_bugs.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoBug{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_BUG_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolEntity := inputRow.(*models.ZentaoBug) + domainEntity := &ticket.Issue{ + DomainEntity: domainlayer.DomainEntity{ + Id: bugIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID), + }, + IssueKey: strconv.FormatInt(toolEntity.ID, 10), + Title: toolEntity.Title, + Type: ticket.BUG, + OriginalStatus: toolEntity.Status, + ResolutionDate: toolEntity.ClosedDate.ToNullableTime(), + CreatedDate: toolEntity.OpenedDate.ToNullableTime(), + UpdatedDate: toolEntity.LastEditedDate.ToNullableTime(), + ParentIssueId: storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Story), + Priority: string(rune(toolEntity.Pri)), + CreatorId: strconv.FormatInt(toolEntity.OpenedById, 10), + CreatorName: toolEntity.OpenedByName, + AssigneeId: strconv.FormatInt(toolEntity.AssignedToId, 10), + AssigneeName: toolEntity.AssignedToName, + Severity: string(rune(toolEntity.Severity)), + } + switch toolEntity.Status { + case "resolved": + domainEntity.Status = ticket.DONE + default: + domainEntity.Status = ticket.IN_PROGRESS + } + if toolEntity.ClosedDate != nil { + domainEntity.LeadTimeMinutes = int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes()) + } + domainBoardIssue := &ticket.BoardIssue{ + BoardId: boardIdGen.Generate(data.Options.ConnectionId, data.Options.ProductId), + IssueId: domainEntity.Id, + } + results := make([]interface{}, 0) + results = append(results, domainEntity, domainBoardIssue) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/bug_extractor.go b/plugins/zentao/tasks/bug_extractor.go new file mode 100644 index 00000000000..7935c5a1792 --- /dev/null +++ b/plugins/zentao/tasks/bug_extractor.go @@ -0,0 +1,138 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractBug + +var ExtractBugMeta = core.SubTaskMeta{ + Name: "extractBug", + EntryPoint: ExtractBug, + EnabledByDefault: true, + Description: "extract Zentao bug", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractBug(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_BUG_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + res := &models.ZentaoBugRes{} + err := json.Unmarshal(row.Data, res) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + bug := &models.ZentaoBug{ + ConnectionId: data.Options.ConnectionId, + ID: res.ID, + Project: res.Project, + Product: res.Product, + Injection: res.Injection, + Identify: res.Identify, + Branch: res.Branch, + Module: res.Module, + Execution: res.Execution, + Plan: res.Plan, + Story: res.Story, + StoryVersion: res.StoryVersion, + Task: res.Task, + ToTask: res.ToTask, + ToStory: res.ToStory, + Title: res.Title, + Keywords: res.Keywords, + Severity: res.Severity, + Pri: res.Pri, + Type: res.Type, + Os: res.Os, + Browser: res.Browser, + Hardware: res.Hardware, + Found: res.Found, + Steps: res.Steps, + Status: res.Status, + SubStatus: res.SubStatus, + Color: res.Color, + Confirmed: res.Confirmed, + ActivatedCount: res.ActivatedCount, + ActivatedDate: res.ActivatedDate, + FeedbackBy: res.FeedbackBy, + NotifyEmail: res.NotifyEmail, + OpenedById: getAccountId(res.OpenedBy), + OpenedByName: getAccountName(res.OpenedBy), + OpenedDate: res.OpenedDate, + OpenedBuild: res.OpenedBuild, + AssignedToId: getAccountId(res.AssignedTo), + AssignedToName: getAccountName(res.AssignedTo), + AssignedDate: res.AssignedDate, + Deadline: res.Deadline, + ResolvedById: getAccountId(res.ResolvedBy), + Resolution: res.Resolution, + ResolvedBuild: res.ResolvedBuild, + ResolvedDate: res.ResolvedDate, + ClosedById: getAccountId(res.ClosedBy), + ClosedDate: res.ClosedDate, + DuplicateBug: res.DuplicateBug, + LinkBug: res.LinkBug, + Feedback: res.Feedback, + Result: res.Result, + Repo: res.Repo, + Mr: res.Mr, + Entry: res.Entry, + NumOfLine: res.NumOfLine, + V1: res.V1, + V2: res.V2, + RepoType: res.RepoType, + IssueKey: res.IssueKey, + Testtask: res.Testtask, + LastEditedById: getAccountId(res.LastEditedBy), + LastEditedDate: res.LastEditedDate, + Deleted: res.Deleted, + PriOrder: res.PriOrder, + SeverityOrder: res.SeverityOrder, + Needconfirm: res.Needconfirm, + StatusName: res.StatusName, + ProductStatus: res.ProductStatus, + } + results := make([]interface{}, 0) + results = append(results, bug) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/department_collector.go b/plugins/zentao/tasks/department_collector.go new file mode 100644 index 00000000000..eacc2779eb9 --- /dev/null +++ b/plugins/zentao/tasks/department_collector.go @@ -0,0 +1,82 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_DEPARTMENT_TABLE = "zentao_api_departments" + +var _ core.SubTaskEntryPoint = CollectDepartment + +func CollectDepartment(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_DEPARTMENT_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "/users", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Users []json.RawMessage `json:"users"` + } + err := helper.UnmarshalResponse(res, &data) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector") + } + return data.Users, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectDepartmentMeta = core.SubTaskMeta{ + Name: "CollectDepartment", + EntryPoint: CollectDepartment, + EnabledByDefault: true, + Description: "Collect Department data from Zentao api", +} diff --git a/plugins/zentao/tasks/department_convertor.go b/plugins/zentao/tasks/department_convertor.go new file mode 100644 index 00000000000..b892bc8220b --- /dev/null +++ b/plugins/zentao/tasks/department_convertor.go @@ -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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/crossdomain" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" +) + +var _ core.SubTaskEntryPoint = ConvertDepartment + +var ConvertDepartmentMeta = core.SubTaskMeta{ + Name: "convertDepartment", + EntryPoint: ConvertDepartment, + EnabledByDefault: true, + Description: "convert Zentao department", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertDepartment(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + departmentIdGen := didgen.NewDomainIdGenerator(&models.ZentaoDepartment{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoDepartment{}), + dal.Where(`_tool_zentao_departments.connection_id = ?`, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoDepartment{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_DEPARTMENT_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolEntity := inputRow.(*models.ZentaoDepartment) + + domainEntity := &crossdomain.Team{ + DomainEntity: domainlayer.DomainEntity{ + Id: departmentIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID), + }, + Name: toolEntity.Name, + ParentId: departmentIdGen.Generate(toolEntity.ConnectionId, toolEntity.Parent), + } + results := make([]interface{}, 0) + results = append(results, domainEntity) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/department_extractor.go b/plugins/zentao/tasks/department_extractor.go new file mode 100644 index 00000000000..8ec1a635596 --- /dev/null +++ b/plugins/zentao/tasks/department_extractor.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractDepartment + +var ExtractDepartmentMeta = core.SubTaskMeta{ + Name: "extractDepartment", + EntryPoint: ExtractDepartment, + EnabledByDefault: true, + Description: "extract Zentao department", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractDepartment(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_DEPARTMENT_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + department := &models.ZentaoDepartment{} + err := json.Unmarshal(row.Data, department) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + department.ConnectionId = data.Options.ConnectionId + results := make([]interface{}, 0) + results = append(results, department) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/execution_collector.go b/plugins/zentao/tasks/execution_collector.go new file mode 100644 index 00000000000..8f6d2c6aedd --- /dev/null +++ b/plugins/zentao/tasks/execution_collector.go @@ -0,0 +1,81 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "io" + "net/http" + "net/url" +) + +const RAW_EXECUTION_TABLE = "zentao_api_executions" + +var _ core.SubTaskEntryPoint = CollectExecution + +func CollectExecution(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_EXECUTION_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "executions/{{ .Params.ExecutionId }}", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao execution collector") + } + res.Body.Close() + return []json.RawMessage{body}, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectExecutionMeta = core.SubTaskMeta{ + Name: "CollectExecution", + EntryPoint: CollectExecution, + EnabledByDefault: true, + Description: "Collect Execution data from Zentao api", +} diff --git a/plugins/zentao/tasks/execution_convertor.go b/plugins/zentao/tasks/execution_convertor.go new file mode 100644 index 00000000000..b663dfec0a4 --- /dev/null +++ b/plugins/zentao/tasks/execution_convertor.go @@ -0,0 +1,93 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" +) + +var _ core.SubTaskEntryPoint = ConvertExecutions + +var ConvertExecutionMeta = core.SubTaskMeta{ + Name: "convertExecutions", + EntryPoint: ConvertExecutions, + EnabledByDefault: true, + Description: "convert Zentao executions", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertExecutions(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoExecution{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoExecution{}), + dal.Where(`_tool_zentao_executions.id = ? and + _tool_zentao_executions.connection_id = ?`, data.Options.ExecutionId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoExecution{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_EXECUTION_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolExecution := inputRow.(*models.ZentaoExecution) + + domainBoard := &ticket.Board{ + DomainEntity: domainlayer.DomainEntity{ + Id: boardIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id), + }, + Name: toolExecution.Name, + Description: toolExecution.Description, + Url: toolExecution.Path, + CreatedDate: toolExecution.OpenedDate.ToNullableTime(), + Type: toolExecution.Type, + } + + results := make([]interface{}, 0) + results = append(results, domainBoard) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/execution_extractor.go b/plugins/zentao/tasks/execution_extractor.go new file mode 100644 index 00000000000..6ed94b87094 --- /dev/null +++ b/plugins/zentao/tasks/execution_extractor.go @@ -0,0 +1,127 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractExecutions + +var ExtractExecutionMeta = core.SubTaskMeta{ + Name: "extractExecutions", + EntryPoint: ExtractExecutions, + EnabledByDefault: true, + Description: "extract Zentao executions", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractExecutions(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_EXECUTION_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + res := &models.ZentaoExecutionRes{} + err := json.Unmarshal(row.Data, res) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + execution := &models.ZentaoExecution{ + ConnectionId: data.Options.ConnectionId, + Id: res.ID, + Project: res.Project, + Model: res.Model, + Type: res.Type, + Lifetime: res.Lifetime, + Budget: res.Budget, + BudgetUnit: res.BudgetUnit, + Attribute: res.Attribute, + Percent: res.Percent, + Milestone: res.Milestone, + Output: res.Output, + Auth: res.Auth, + Parent: res.Parent, + Path: res.Path, + Grade: res.Grade, + Name: res.Name, + Code: res.Code, + PlanBegin: res.PlanBegin, + PlanEnd: res.PlanEnd, + RealBegan: res.RealBegan, + RealEnd: res.RealEnd, + Status: res.Status, + SubStatus: res.SubStatus, + Pri: res.Pri, + Description: res.Description, + Version: res.Version, + ParentVersion: res.ParentVersion, + PlanDuration: res.PlanDuration, + RealDuration: res.RealDuration, + OpenedById: getAccountId(res.OpenedBy), + OpenedDate: res.OpenedDate, + OpenedVersion: res.OpenedVersion, + LastEditedById: getAccountId(res.LastEditedBy), + LastEditedDate: res.LastEditedDate, + ClosedById: getAccountId(res.ClosedBy), + ClosedDate: res.ClosedDate, + CanceledById: getAccountId(res.CanceledBy), + CanceledDate: res.CanceledDate, + SuspendedDate: res.SuspendedDate, + POId: getAccountId(res.PO), + PMId: getAccountId(res.PM), + QDId: getAccountId(res.QD), + RDId: getAccountId(res.RD), + Team: res.Team, + Acl: res.Acl, + OrderIn: res.OrderIn, + Vision: res.Vision, + DisplayCards: res.DisplayCards, + FluidBoard: res.FluidBoard, + Deleted: res.Deleted, + TotalHours: res.TotalHours, + TotalEstimate: res.TotalEstimate, + TotalConsumed: res.TotalConsumed, + TotalLeft: res.TotalLeft, + Progress: res.Progress, + CaseReview: res.CaseReview, + } + results := make([]interface{}, 0) + results = append(results, execution) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/product_collector.go b/plugins/zentao/tasks/product_collector.go new file mode 100644 index 00000000000..cc1e9d8cb0f --- /dev/null +++ b/plugins/zentao/tasks/product_collector.go @@ -0,0 +1,80 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "io" + "net/http" + "net/url" +) + +const RAW_PRODUCT_TABLE = "zentao_api_products" + +var _ core.SubTaskEntryPoint = CollectProduct + +func CollectProduct(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ProductId: data.Options.ProductId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_PRODUCT_TABLE, + }, + ApiClient: data.ApiClient, + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "products/{{ .Params.ProductId }}", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao product collector") + } + res.Body.Close() + return []json.RawMessage{body}, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectProductMeta = core.SubTaskMeta{ + Name: "CollectProduct", + EntryPoint: CollectProduct, + EnabledByDefault: true, + Description: "Collect Product data from Zentao api", +} diff --git a/plugins/zentao/tasks/product_convertor.go b/plugins/zentao/tasks/product_convertor.go new file mode 100644 index 00000000000..afbb946317b --- /dev/null +++ b/plugins/zentao/tasks/product_convertor.go @@ -0,0 +1,92 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" +) + +var _ core.SubTaskEntryPoint = ConvertProducts + +var ConvertProductMeta = core.SubTaskMeta{ + Name: "convertProducts", + EntryPoint: ConvertProducts, + EnabledByDefault: true, + Description: "convert Zentao products", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertProducts(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoProduct{}), + dal.Where(`_tool_zentao_products.id = ? and + _tool_zentao_products.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoProduct{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ProductId: data.Options.ProductId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_PRODUCT_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolProduct := inputRow.(*models.ZentaoProduct) + + domainBoard := &ticket.Board{ + DomainEntity: domainlayer.DomainEntity{ + Id: boardIdGen.Generate(toolProduct.ConnectionId, toolProduct.Id), + }, + Name: toolProduct.Name, + Description: toolProduct.Description, + CreatedDate: toolProduct.CreatedDate.ToNullableTime(), + Type: toolProduct.Type, + } + + results := make([]interface{}, 0) + results = append(results, domainBoard) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/product_extractor.go b/plugins/zentao/tasks/product_extractor.go new file mode 100644 index 00000000000..88428e37b2a --- /dev/null +++ b/plugins/zentao/tasks/product_extractor.go @@ -0,0 +1,101 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractProducts + +var ExtractProductMeta = core.SubTaskMeta{ + Name: "extractProducts", + EntryPoint: ExtractProducts, + EnabledByDefault: true, + Description: "extract Zentao products", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractProducts(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ProductId: data.Options.ProductId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_PRODUCT_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + res := &models.ZentaoProductRes{} + err := json.Unmarshal(row.Data, res) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao product extractor") + } + product := &models.ZentaoProduct{ + ConnectionId: data.Options.ConnectionId, + Id: int64(res.ID), + Program: res.Program, + Name: res.Name, + Code: res.Code, + Bind: res.Bind, + Line: res.Line, + Type: res.Type, + Status: res.Status, + SubStatus: res.SubStatus, + Description: res.Description, + POId: getAccountId(res.PO), + QDId: getAccountId(res.QD), + RDId: getAccountId(res.RD), + Acl: res.Acl, + Reviewer: res.Reviewer, + CreatedById: getAccountId(res.CreatedBy), + CreatedDate: res.CreatedDate, + CreatedVersion: res.CreatedVersion, + OrderIn: res.OrderIn, + Deleted: res.Deleted, + Plans: res.Plans, + Releases: res.Releases, + Builds: res.Builds, + Cases: res.Cases, + Projects: res.Projects, + Executions: res.Executions, + Bugs: res.Bugs, + Docs: res.Docs, + Progress: res.Progress, + CaseReview: res.CaseReview, + } + results := make([]interface{}, 0) + results = append(results, product) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/project_collector.go b/plugins/zentao/tasks/project_collector.go new file mode 100644 index 00000000000..6d737a0b5fa --- /dev/null +++ b/plugins/zentao/tasks/project_collector.go @@ -0,0 +1,79 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_PROJECT_TABLE = "zentao_api_projects" + +var _ core.SubTaskEntryPoint = CollectProject + +func CollectProject(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ProductId: data.Options.ProductId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_PROJECT_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "projects", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Projects []json.RawMessage `json:"projects"` + } + err := helper.UnmarshalResponse(res, &data) + return data.Projects, err + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectProjectMeta = core.SubTaskMeta{ + Name: "CollectProject", + EntryPoint: CollectProject, + EnabledByDefault: true, + Description: "Collect Project data from Zentao api", +} diff --git a/plugins/zentao/tasks/project_extractor.go b/plugins/zentao/tasks/project_extractor.go new file mode 100644 index 00000000000..ce1463471f0 --- /dev/null +++ b/plugins/zentao/tasks/project_extractor.go @@ -0,0 +1,69 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractProjects + +var ExtractProjectMeta = core.SubTaskMeta{ + Name: "extractProjects", + EntryPoint: ExtractProjects, + EnabledByDefault: true, + Description: "extract Zentao projects", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractProjects(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ProductId: data.Options.ProductId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_PROJECT_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + project := &models.ZentaoProject{} + err := json.Unmarshal(row.Data, project) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao project executor") + } + project.ConnectionId = data.Options.ConnectionId + results := make([]interface{}, 0) + results = append(results, project) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/shared.go b/plugins/zentao/tasks/shared.go new file mode 100644 index 00000000000..39fc8631834 --- /dev/null +++ b/plugins/zentao/tasks/shared.go @@ -0,0 +1,50 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "net/http" + + "github.com/apache/incubator-devlake/plugins/helper" +) + +func GetTotalPagesFromResponse(res *http.Response, args *helper.ApiCollectorArgs) (int, errors.Error) { + body := &ZentaoPagination{} + err := helper.UnmarshalResponse(res, body) + if err != nil { + return 0, err + } + return body.Page, nil + +} + +func getAccountId(account *models.ZentaoAccount) int64 { + if account != nil { + return account.ID + } + return 0 +} + +func getAccountName(account *models.ZentaoAccount) string { + if account != nil { + return account.Realname + } + return "" +} diff --git a/plugins/zentao/tasks/story_collector.go b/plugins/zentao/tasks/story_collector.go new file mode 100644 index 00000000000..368e375d3f6 --- /dev/null +++ b/plugins/zentao/tasks/story_collector.go @@ -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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_STORY_TABLE = "zentao_api_stories" + +var _ core.SubTaskEntryPoint = CollectStory + +func CollectStory(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_STORY_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "/products/{{ .Params.ProductId }}/stories", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + query.Set("status", "allstory") + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Story []json.RawMessage `json:"stories"` + } + err := helper.UnmarshalResponse(res, &data) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector") + } + return data.Story, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectStoryMeta = core.SubTaskMeta{ + Name: "CollectStory", + EntryPoint: CollectStory, + EnabledByDefault: true, + Description: "Collect Story data from Zentao api", +} diff --git a/plugins/zentao/tasks/story_convertor.go b/plugins/zentao/tasks/story_convertor.go new file mode 100644 index 00000000000..15726f0cb1f --- /dev/null +++ b/plugins/zentao/tasks/story_convertor.go @@ -0,0 +1,117 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" + "strconv" +) + +var _ core.SubTaskEntryPoint = ConvertStory + +var ConvertStoryMeta = core.SubTaskMeta{ + Name: "convertStory", + EntryPoint: ConvertStory, + EnabledByDefault: true, + Description: "convert Zentao story", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertStory(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + storyIdGen := didgen.NewDomainIdGenerator(&models.ZentaoStory{}) + boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoStory{}), + dal.Where(`_tool_zentao_stories.product = ? and + _tool_zentao_stories.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoStory{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_STORY_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolEntity := inputRow.(*models.ZentaoStory) + + domainEntity := &ticket.Issue{ + DomainEntity: domainlayer.DomainEntity{ + Id: storyIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID), + }, + IssueKey: strconv.FormatInt(toolEntity.ID, 10), + Title: toolEntity.Title, + Type: ticket.REQUIREMENT, + OriginalStatus: toolEntity.Stage, + ResolutionDate: toolEntity.ClosedDate.ToNullableTime(), + CreatedDate: toolEntity.OpenedDate.ToNullableTime(), + UpdatedDate: toolEntity.LastEditedDate.ToNullableTime(), + ParentIssueId: storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent), + Priority: string(rune(toolEntity.Pri)), + CreatorId: strconv.FormatInt(toolEntity.OpenedById, 10), + CreatorName: toolEntity.OpenedByName, + AssigneeId: strconv.FormatInt(toolEntity.AssignedToId, 10), + AssigneeName: toolEntity.AssignedToName, + } + switch toolEntity.Stage { + case "closed": + domainEntity.Status = ticket.DONE + case "wait": + domainEntity.Status = ticket.TODO + default: + domainEntity.Status = ticket.IN_PROGRESS + } + if toolEntity.ClosedDate != nil { + domainEntity.LeadTimeMinutes = int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes()) + } + domainBoardIssue := &ticket.BoardIssue{ + BoardId: boardIdGen.Generate(data.Options.ConnectionId, data.Options.ProductId), + IssueId: domainEntity.Id, + } + results := make([]interface{}, 0) + results = append(results, domainEntity, domainBoardIssue) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/story_extractor.go b/plugins/zentao/tasks/story_extractor.go new file mode 100644 index 00000000000..9495b9b630e --- /dev/null +++ b/plugins/zentao/tasks/story_extractor.go @@ -0,0 +1,125 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractStory + +var ExtractStoryMeta = core.SubTaskMeta{ + Name: "extractStory", + EntryPoint: ExtractStory, + EnabledByDefault: true, + Description: "extract Zentao story", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractStory(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_STORY_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + res := &models.ZentaoStoryRes{} + err := json.Unmarshal(row.Data, res) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + story := &models.ZentaoStory{ + ConnectionId: data.Options.ConnectionId, + ID: res.ID, + Product: res.Product, + Branch: res.Branch, + Version: res.Version, + OrderIn: 0, + Vision: res.Vision, + Parent: res.Parent, + Module: res.Module, + Plan: res.Plan, + Source: res.Source, + SourceNote: res.SourceNote, + FromBug: res.FromBug, + Feedback: res.Feedback, + Title: res.Title, + Keywords: res.Keywords, + Type: res.Type, + Category: res.Category, + Pri: res.Pri, + Estimate: res.Estimate, + Status: res.Status, + SubStatus: res.SubStatus, + Color: res.Color, + Stage: res.Stage, + Lib: res.Lib, + FromStory: res.FromStory, + FromVersion: res.FromVersion, + OpenedById: getAccountId(res.OpenedBy), + OpenedByName: getAccountName(res.OpenedBy), + OpenedDate: res.OpenedDate, + AssignedToId: getAccountId(res.AssignedTo), + AssignedToName: getAccountName(res.AssignedTo), + AssignedDate: res.AssignedDate, + ApprovedDate: res.ApprovedDate, + LastEditedId: getAccountId(res.LastEditedBy), + LastEditedDate: res.LastEditedDate, + ChangedDate: res.ChangedDate, + ReviewedById: getAccountId(res.ReviewedBy), + ReviewedDate: res.ReviewedDate, + ClosedId: getAccountId(res.ClosedBy), + ClosedDate: res.ClosedDate, + ClosedReason: res.ClosedReason, + ActivatedDate: res.ActivatedDate, + ToBug: res.ToBug, + ChildStories: res.ChildStories, + LinkStories: res.LinkStories, + LinkRequirements: res.LinkRequirements, + DuplicateStory: res.DuplicateStory, + StoryChanged: res.StoryChanged, + FeedbackBy: res.FeedbackBy, + NotifyEmail: res.NotifyEmail, + URChanged: res.URChanged, + Deleted: res.Deleted, + PriOrder: res.PriOrder, + PlanTitle: res.PlanTitle, + } + results := make([]interface{}, 0) + results = append(results, story) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/tasks/task_collector.go b/plugins/zentao/tasks/task_collector.go new file mode 100644 index 00000000000..8094870cfd6 --- /dev/null +++ b/plugins/zentao/tasks/task_collector.go @@ -0,0 +1,82 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "fmt" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "net/http" + "net/url" +) + +const RAW_TASK_TABLE = "zentao_api_tasks" + +var _ core.SubTaskEntryPoint = CollectTask + +func CollectTask(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + collector, err := helper.NewApiCollector(helper.ApiCollectorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_TASK_TABLE, + }, + ApiClient: data.ApiClient, + + PageSize: 100, + // TODO write which api would you want request + UrlTemplate: "/executions/{{ .Params.ExecutionId }}/tasks", + Query: func(reqData *helper.RequestData) (url.Values, errors.Error) { + query := url.Values{} + query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page)) + query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size)) + return query, nil + }, + GetTotalPages: GetTotalPagesFromResponse, + ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) { + var data struct { + Task []json.RawMessage `json:"tasks"` + } + err := helper.UnmarshalResponse(res, &data) + if err != nil { + return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector") + } + return data.Task, nil + }, + }) + if err != nil { + return err + } + + return collector.Execute() +} + +var CollectTaskMeta = core.SubTaskMeta{ + Name: "CollectTask", + EntryPoint: CollectTask, + EnabledByDefault: true, + Description: "Collect Task data from Zentao api", +} diff --git a/plugins/zentao/tasks/task_convertor.go b/plugins/zentao/tasks/task_convertor.go new file mode 100644 index 00000000000..cccedaa1694 --- /dev/null +++ b/plugins/zentao/tasks/task_convertor.go @@ -0,0 +1,119 @@ +/* +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. +*/ + +package tasks + +import ( + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/models/domainlayer" + "github.com/apache/incubator-devlake/models/domainlayer/didgen" + "github.com/apache/incubator-devlake/models/domainlayer/ticket" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/core/dal" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" + "reflect" + "strconv" +) + +var _ core.SubTaskEntryPoint = ConvertTask + +var ConvertTaskMeta = core.SubTaskMeta{ + Name: "convertTask", + EntryPoint: ConvertTask, + EnabledByDefault: true, + Description: "convert Zentao task", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ConvertTask(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + db := taskCtx.GetDal() + storyIdGen := didgen.NewDomainIdGenerator(&models.ZentaoStory{}) + boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{}) + taskIdGen := didgen.NewDomainIdGenerator(&models.ZentaoTask{}) + cursor, err := db.Cursor( + dal.From(&models.ZentaoTask{}), + dal.Where(`_tool_zentao_tasks.execution = ? and + _tool_zentao_tasks.connection_id = ?`, data.Options.ExecutionId, data.Options.ConnectionId), + ) + if err != nil { + return err + } + defer cursor.Close() + convertor, err := helper.NewDataConverter(helper.DataConverterArgs{ + InputRowType: reflect.TypeOf(models.ZentaoTask{}), + Input: cursor, + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_TASK_TABLE, + }, + Convert: func(inputRow interface{}) ([]interface{}, errors.Error) { + toolEntity := inputRow.(*models.ZentaoTask) + + domainEntity := &ticket.Issue{ + DomainEntity: domainlayer.DomainEntity{ + Id: taskIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID), + }, + IssueKey: strconv.FormatInt(toolEntity.ID, 10), + Title: toolEntity.Name, + Description: toolEntity.Description, + Type: "TASK", + OriginalStatus: toolEntity.Status, + ResolutionDate: toolEntity.ClosedDate.ToNullableTime(), + CreatedDate: toolEntity.OpenedDate.ToNullableTime(), + UpdatedDate: toolEntity.LastEditedDate.ToNullableTime(), + ParentIssueId: storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent), + Priority: string(rune(toolEntity.Pri)), + CreatorId: strconv.FormatInt(toolEntity.OpenedById, 10), + CreatorName: toolEntity.OpenedByName, + AssigneeId: strconv.FormatInt(toolEntity.AssignedToId, 10), + AssigneeName: toolEntity.AssignedToName, + } + switch toolEntity.Status { + case "done", "closed", "cancel": + domainEntity.Status = ticket.DONE + case "wait": + domainEntity.Status = ticket.TODO + default: + domainEntity.Status = ticket.IN_PROGRESS + } + if toolEntity.ClosedDate != nil { + domainEntity.LeadTimeMinutes = int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes()) + } + domainBoardIssue := &ticket.BoardIssue{ + BoardId: boardIdGen.Generate(data.Options.ConnectionId, data.Options.ProductId), + IssueId: domainEntity.Id, + } + results := make([]interface{}, 0) + results = append(results, domainEntity, domainBoardIssue) + return results, nil + }, + }) + + if err != nil { + return err + } + + return convertor.Execute() +} diff --git a/plugins/zentao/tasks/task_data.go b/plugins/zentao/tasks/task_data.go new file mode 100644 index 00000000000..aa75c84f4ed --- /dev/null +++ b/plugins/zentao/tasks/task_data.go @@ -0,0 +1,62 @@ +/* +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. +*/ + +package tasks + +import ( + "fmt" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/mitchellh/mapstructure" +) + +type ZentaoApiParams struct { + ConnectionId uint64 + ProductId int64 + ExecutionId int64 + ProjectId int64 +} + +type ZentaoOptions struct { + // TODO add some custom options here if necessary + // options means some custom params required by plugin running. + // Such As How many rows do your want + // You can use it in sub tasks and you need pass it in main.go and pipelines. + ConnectionId uint64 `json:"connectionId"` + ProductId int64 + ExecutionId int64 + ProjectId int64 + Tasks []string `json:"tasks,omitempty"` + Since string +} + +type ZentaoTaskData struct { + Options *ZentaoOptions + ApiClient *helper.ApiAsyncClient +} + +func DecodeAndValidateTaskOptions(options map[string]interface{}) (*ZentaoOptions, error) { + var op ZentaoOptions + err := mapstructure.Decode(options, &op) + if err != nil { + return nil, err + } + + if op.ConnectionId == 0 { + return nil, fmt.Errorf("connectionId is invalid") + } + return &op, nil +} diff --git a/plugins/zentao/tasks/task_extractor.go b/plugins/zentao/tasks/task_extractor.go new file mode 100644 index 00000000000..df131895077 --- /dev/null +++ b/plugins/zentao/tasks/task_extractor.go @@ -0,0 +1,138 @@ +/* +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. +*/ + +package tasks + +import ( + "encoding/json" + "github.com/apache/incubator-devlake/errors" + "github.com/apache/incubator-devlake/plugins/core" + "github.com/apache/incubator-devlake/plugins/helper" + "github.com/apache/incubator-devlake/plugins/zentao/models" +) + +var _ core.SubTaskEntryPoint = ExtractTask + +var ExtractTaskMeta = core.SubTaskMeta{ + Name: "extractTask", + EntryPoint: ExtractTask, + EnabledByDefault: true, + Description: "extract Zentao task", + DomainTypes: []string{core.DOMAIN_TYPE_TICKET}, +} + +func ExtractTask(taskCtx core.SubTaskContext) errors.Error { + data := taskCtx.GetData().(*ZentaoTaskData) + extractor, err := helper.NewApiExtractor(helper.ApiExtractorArgs{ + RawDataSubTaskArgs: helper.RawDataSubTaskArgs{ + Ctx: taskCtx, + Params: ZentaoApiParams{ + ConnectionId: data.Options.ConnectionId, + ProductId: data.Options.ProductId, + ExecutionId: data.Options.ExecutionId, + ProjectId: data.Options.ProjectId, + }, + Table: RAW_TASK_TABLE, + }, + Extract: func(row *helper.RawData) ([]interface{}, errors.Error) { + res := &models.ZentaoTaskRes{} + err := json.Unmarshal(row.Data, res) + if err != nil { + return nil, errors.Default.WrapRaw(err) + } + task := &models.ZentaoTask{ + ConnectionId: data.Options.ConnectionId, + ExecutionId: data.Options.ExecutionId, + ID: res.Id, + Project: res.Project, + Parent: res.Parent, + Execution: res.Execution, + Module: res.Module, + Design: res.Design, + Story: res.Story, + StoryVersion: res.StoryVersion, + DesignVersion: res.DesignVersion, + FromBug: res.FromBug, + Feedback: res.Feedback, + FromIssue: res.FromIssue, + Name: res.Name, + Type: res.Type, + Mode: res.Mode, + Pri: res.Pri, + Estimate: res.Estimate, + Consumed: res.Consumed, + Deadline: res.Deadline, + Status: res.Status, + SubStatus: res.SubStatus, + Color: res.Color, + Description: res.Description, + Version: res.Version, + OpenedById: getAccountId(res.OpenedBy), + OpenedByName: getAccountName(res.OpenedBy), + OpenedDate: res.OpenedDate, + AssignedToId: getAccountId(res.AssignedTo), + AssignedToName: getAccountName(res.AssignedTo), + AssignedDate: res.AssignedDate, + EstStarted: res.EstStarted, + RealStarted: res.RealStarted, + FinishedId: getAccountId(res.FinishedBy), + FinishedDate: res.FinishedDate, + FinishedList: res.FinishedList, + CanceledId: getAccountId(res.CanceledBy), + CanceledDate: res.CanceledDate, + ClosedById: getAccountId(res.ClosedBy), + ClosedDate: res.ClosedDate, + PlanDuration: res.PlanDuration, + RealDuration: res.RealDuration, + ClosedReason: res.ClosedReason, + LastEditedId: getAccountId(res.LastEditedBy), + LastEditedDate: res.LastEditedDate, + ActivatedDate: res.ActivatedDate, + OrderIn: res.OrderIn, + Repo: res.Repo, + Mr: res.Mr, + Entry: res.Entry, + NumOfLine: res.NumOfLine, + V1: res.V1, + V2: res.V2, + Deleted: res.Deleted, + Vision: res.Vision, + StoryID: res.Story, + StoryTitle: res.StoryTitle, + LatestStoryVersion: 0, + //Product: getAccountId(res.Product), + //Branch: res.Branch, + //LatestStoryVersion: res.LatestStoryVersion, + //StoryStatus: res.StoryStatus, + AssignedToRealName: res.AssignedToRealName, + PriOrder: res.PriOrder, + NeedConfirm: res.NeedConfirm, + //ProductType: res.ProductType, + Progress: res.Progress, + } + results := make([]interface{}, 0) + results = append(results, task) + return results, nil + }, + }) + + if err != nil { + return err + } + + return extractor.Execute() +} diff --git a/plugins/zentao/zentao.go b/plugins/zentao/zentao.go new file mode 100644 index 00000000000..11d73050a17 --- /dev/null +++ b/plugins/zentao/zentao.go @@ -0,0 +1,47 @@ +/* +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. +*/ + +package main + +import ( + "github.com/apache/incubator-devlake/plugins/zentao/impl" + "github.com/apache/incubator-devlake/runner" + "github.com/spf13/cobra" +) + +// PluginEntry Export a variable named PluginEntry for Framework to search and load +var PluginEntry impl.Zentao //nolint + +// standalone mode for debugging +func main() { + cmd := &cobra.Command{Use: "zentao"} + + connectionId := cmd.Flags().Uint64P("connectionId", "c", 0, "zentao connection id") + executionId := cmd.Flags().IntP("executionId", "e", 8, "execution id") + productId := cmd.Flags().IntP("productId", "o", 8, "product id") + projectId := cmd.Flags().IntP("projectId", "p", 8, "project id") + + cmd.Run = func(cmd *cobra.Command, args []string) { + runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{ + "connectionId": *connectionId, + "executionId": *executionId, + "productId": *productId, + "projectId": *projectId, + }) + } + runner.RunCmd(cmd) +}