Skip to content

Commit dfd2b88

Browse files
committed
feat(WorkItemCloneCommand.cs): add support for custom query parameters in CreateProjectQuery
feat(WorkItemCloneCommandSettings.cs): add new command options for targetQuery, targetQueryTitle, targetQueryFolder feat(WorkItemCommandBase.cs): add support for new command options in config feat(configuration.json): add new fields for targetQuery, targetQueryTitle, targetQueryFolder feat(AzureDevOpsApi.cs): add support for replacing parameters in query strings docs(README.md): update documentation to reflect new command options and configuration fields
1 parent 1323590 commit dfd2b88

6 files changed

Lines changed: 83 additions & 50 deletions

File tree

AzureDevOps.WorkItemClone.ConsoleUI/Commands/WorkItemCloneCommand.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,15 @@ await AnsiConsole.Progress()
226226
// Task 7: Create Query
227227
task7.MaxValue = 1;
228228
task7.StartTask();
229-
var query = await targetApi.CreateProjectQuery($"Project [{projectItem.id}]: {projectItem.fields.SystemTitle}", "Select [System.Id] From WorkItems Where [System.TeamProject] = '@project' AND [System.Parent] = @id", new Dictionary<string, string>() { { "@id", config.templateParentId.ToString() } });
229+
230+
Dictionary<string, string> queryParameters = new Dictionary<string, string>()
231+
{
232+
{ "@projectID", projectItem.id.ToString() },
233+
{ "@projectTitle", projectItem.fields.SystemTitle },
234+
{ "@projectTags", projectItem.fields.SystemTags },
235+
{ "@RunName", config.RunName }
236+
};
237+
var query = await targetApi.CreateProjectQuery(config.targetQueryTitle, config.targetQuery, queryParameters);
230238
task7.Increment(1);
231239
task7.StopTask();
232240

AzureDevOps.WorkItemClone.ConsoleUI/Commands/WorkItemCloneCommandSettings.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ internal class WorkItemCloneCommandSettings : BaseCommandSettings
4242
[CommandOption("--targetFalbackWit")]
4343
[DefaultValue("Deliverable")]
4444
public string? targetFalbackWit { get; set; }
45+
46+
47+
[Description("The WIQL Query to use. You can use @projectID, @projectTitle, @projectTags to replace data from the project!")]
48+
[CommandOption("--targetQuery")]
49+
[DefaultValue("SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AreaPath],[System.AssignedTo],[System.State] FROM workitems WHERE [System.Parent] = @projectID")]
50+
public string? targetQuery { get; set; }
51+
52+
[Description("The title to use for the query. You can use @projectID, @projectTitle, @projectTags, @RunName to replace data from the project!")]
53+
[CommandOption("--targetQueryTitle")]
54+
[DefaultValue("Project-@RunName - @projectTitle")]
55+
public string? targetQueryTitle { get; set; }
56+
57+
[Description("Must already Exist and be in the form 'Shared Queries/Folder1/Folder2'!")]
58+
[CommandOption("--targetQueryFolder")]
59+
[DefaultValue("Shared Queries")]
60+
public string? targetQueryFolder { get; set; }
61+
4562
//------------------------------------------------
4663
[Description("The access token for the template location")]
4764
[CommandOption("--templateAccessToken")]

AzureDevOps.WorkItemClone.ConsoleUI/Commands/WorkItemCommandBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ internal void CombineValuesFromConfigAndSettings(WorkItemCloneCommandSettings se
3737
config.targetParentId = EnsureIntAskIfMissing(config.targetParentId = settings.targetParentId != null ? settings.targetParentId : config.targetParentId, "Provide the target parent?");
3838
config.targetFalbackWit = EnsureStringAskIfMissing(config.targetFalbackWit = settings.targetFalbackWit != null ? settings.targetFalbackWit : config.targetFalbackWit, "Provide the target fallback wit?");
3939

40+
config.targetQueryTitle = EnsureStringAskIfMissing(config.targetQueryTitle = settings.targetQueryTitle != null ? settings.targetQueryTitle : config.targetQueryTitle, "Provide the target query title?");
41+
config.targetQueryFolder = EnsureStringAskIfMissing(config.targetQueryFolder = settings.targetQueryFolder != null ? settings.targetQueryFolder : config.targetQueryFolder, "Provide the target query folder?");
42+
config.targetQuery = EnsureStringAskIfMissing(config.targetQuery = settings.targetQuery != null ? settings.targetQuery : config.targetQuery, "Provide the target WIQL query?");
4043
}
4144

4245

AzureDevOps.WorkItemClone.ConsoleUI/configuration.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@
88
"templateAccessToken": null,
99
"templateOrganization": "ABB-MO-ATE",
1010
"templateProject": "ABB Traction Template",
11-
"templateParentId": 212315
11+
"templateParentId": 212315,
12+
"targetQuery": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AreaPath],[System.AssignedTo],[System.State] FROM workitems WHERE [System.Parent] = @projectID",
13+
"targetQueryTitle": "Project-@RunName - @projectTitle",
14+
"targetQueryFolder": "Shared Queries"
15+
1216
}

AzureDevOps.WorkItemClone/AzureDevOpsApi.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,23 @@ private string GetQueryString( string wiqlQuery, Dictionary<string, string> par
7676
{
7777
wiqlQuery = "Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.TeamProject] = '@project' order by [System.CreatedDate] desc";
7878
}
79+
wiqlQuery = ReplaceParamsInString(wiqlQuery, parameters);
80+
return wiqlQuery;
81+
}
82+
private string ReplaceParamsInString(string text, Dictionary<string, string> parameters)
83+
{
84+
if (string.IsNullOrEmpty(text))
85+
{
86+
text = "Default";
87+
}
7988
foreach (var param in parameters)
8089
{
81-
wiqlQuery = wiqlQuery.Replace(param.Key, param.Value);
90+
text = text.Replace(param.Key, param.Value);
8291
}
83-
return wiqlQuery;
92+
return text;
8493
}
8594

95+
8696
public async Task<QueryResults?> GetWiqlQueryResults()
8797
{
8898
string post = JsonConvert.SerializeObject(new {
@@ -241,12 +251,15 @@ public async Task<Query> CreateProjectQuery(string queryName, string wiqlQuery,
241251
{
242252
///POST https://dev.azure.com/{organization}/{project}/_apis/wit/queries/{query}?api-version=7.1-preview.2
243253
wiqlQuery = GetQueryString(wiqlQuery, parameters);
254+
queryName = ReplaceParamsInString(queryName, parameters);
244255
string post = JsonConvert.SerializeObject(new
245256
{
257+
isFolder = false,
246258
name = queryName,
247-
query = wiqlQuery
259+
path = $"Shared Queries/{queryName}",
260+
wiql = wiqlQuery
248261
});
249-
string apiCallUrl = $"https://dev.azure.com/{_account}/{_project}/_apis/wit/queries/Shared Queries/some/?api-version=7.2-preview.2";
262+
string apiCallUrl = $"https://dev.azure.com/{_account}/{_project}/_apis/wit/queries/Shared Queries/?api-version=7.2-preview.2";
250263
var result = await GetObjectResult<Query>(apiCallUrl, post);
251264
return result.result;
252265
}

README.md

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Clones work items from a template project to a target project incorproating a JS
3434
- `--targetOrganization` - The name of the organisation to clone work items to.
3535
- `--targetProject` - The name of the prject to clone work items to.
3636
- `--targetParentId` - All cloned work items will be come a child of this work item
37+
- `--targetQuery` - The query to create in the target project. Default is `SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AreaPath],[System.AssignedTo],[System.State] FROM workitems WHERE [System.Parent] = @projectID`.
38+
- `--targetQueryTitle` - The title of the query to create in the target project. Default is `Project-@RunName - @projectTitle`.
39+
- `--targetQueryFolder` - The folder to create the query in the target project. Default is `Shared Queries`.
3740

3841
*Optional Parameters* - These are optional parameters that can be used to control the behaviour of the clone process.
3942

@@ -83,66 +86,51 @@ Clones work items from a template project to a target project incorproating a JS
8386
```json
8487
{
8588
"CachePath": "./cache",
86-
"inputJsonFile": "ADO_TESTProjPipline_V03.json",
87-
"targetAccessToken": null,
89+
"inputJsonFile": "TESTProjPipline_V03.json",
90+
"targetAccessToken": "************************************",
8891
"targetOrganization": "nkdagility-preview",
8992
"targetProject": "Clone-Demo",
9093
"targetParentId": 540,
91-
"templateAccessToken": null,
92-
"templateOrganization": "Clone-MO-ATE",
93-
"templateProject": "Clone Template"
94+
"templateAccessToken": "************************************",
95+
"templateOrganization": "orgname",
96+
"templateProject": "template Project",
97+
"templateParentId": 212315,
98+
"targetQuery": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AreaPath],[System.AssignedTo],[System.State] FROM workitems WHERE [System.Parent] = @projectID",
99+
"targetQueryTitle": "Project-@RunName - @projectTitle",
100+
"targetQueryFolder": "Shared Queries"
94101
}
95102
```
96103

97-
## inputJsonFile Example
104+
## Json Input File
105+
106+
107+
The `id` is the ID of the template item. This will be used to specifiy Description, Acceptance Criteria, and dependancy relationsips. I the `id` is not specified a new work item will be created.
108+
109+
The `fields` are the fields that will be used to create the work item. You can use any field ientifyer from Azure DevOps.
98110

99111
```json
100-
[
112+
[
101113
{
102114
"id": 213928,
103-
"area": "TPL",
104-
"tags": "Customer Document",
105115
"fields": {
106-
"title": "Technical specification",
107-
"product": "CC000_000A01"
116+
"System.AreaPath": "Engineering Group\\ECH Group\\ECH TPL 1",
117+
"System.Tags": "Customer Document",
118+
"System.Title": "Technical specification",
119+
"Custom.Product": "CC",
120+
"Microsoft.VSTS.Scheduling.Effort": 12,
121+
"Custom.TRA_Milestone": "E0.1"
108122
}
109123
},
110124
{
111-
"id": 213928,
112-
"area": "TPL",
113-
"tags": "Customer Document",
125+
"id": "",
114126
"fields": {
115-
"title": "Technical specification",
116-
"product": "CC000_000A02"
127+
"System.AreaPath": "Engineering Group\\ECH Group\\ECH TPL 1",
128+
"System.Tags": "",
129+
"System.Title": "E4.8 Assessment",
130+
"Custom.Product": "",
131+
"Microsoft.VSTS.Scheduling.Effort": 2,
132+
"Custom.TRA_Milestone": "E4.8"
117133
}
118-
}
119-
]
120-
```
121-
122-
proposed new format not yet adopted:
123-
124-
125-
```json
126-
[
127-
{
128-
"templateId": 213928,
129-
"fields": [
130-
{"System.Title": "Technical specification"},
131-
{"Custom.Project": "CC000_000A01"},
132-
{"System.Tags": "Customer Document"},
133-
{"System.AreaPath": "#{targetProject}#\\TPL"}
134-
]
135-
},
136-
{
137-
"templateId": 213928,
138-
"fields": [
139-
{"System.Title": "Technical specification"},
140-
{"Custom.Project": "CC000_000A02"},
141-
{"System.Tags": "Technical specification"},
142-
{"System.AreaPath": "#{targetProject}#\\TPL"}
143-
]
144-
},
145-
}
146134
]
147135
```
148136

0 commit comments

Comments
 (0)