Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 6a65092

Browse files
kananbAdamL-Microsoft
authored andcommitted
Support custom ado fields that mark work items as duplicate (#3467)
* Add field to ado config for checking duplicate work items * Make duplicate fields nullable and add it to python models * Update broken tests * Update docs to include new ado_duplicate_fields property
1 parent b4ce3ef commit 6a65092

File tree

7 files changed

+28
-5
lines changed

7 files changed

+28
-5
lines changed

contrib/onefuzz-job-azure-devops-pipeline/ado-work-items.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
"System.AreaPath": "OneFuzz-Ado-Integration",
1414
"System.Title": "{{report.task_id}}"
1515
},
16+
"ado_duplicate_fields": {
17+
"System.Reason": "My custom value that means a work item is a duplicate",
18+
"Custom.Work.Item.Field": "My custom value that means a work item is a duplicate"
19+
},
1620
"on_duplicate": {
1721
"increment": [],
1822
"comment": "DUP {{report.input_sha256}} <br> Repro Command: <br> <pre> {{ repro_cmd }} </pre> ",

docs/notifications/ado.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ clickable, make it a link.
5151
"System.Title": "{{ report.crash_site }} - {{ report.executable }}",
5252
"Microsoft.VSTS.TCM.ReproSteps": "This is my call stack: <ul> {{ for item in report.call_stack }} <li> {{ item }} </li> {{ end }} </ul>"
5353
},
54+
"ado_duplicate_fields": {
55+
"System.Reason": "My custom value that means a work item is a duplicate",
56+
"Custom.Work.Item.Field": "My custom value that means a work item is a duplicate"
57+
// note: the fields and values below are checked by default and don't need to be specified
58+
// "System.Reason": "Duplicate"
59+
// "Microsoft.VSTS.Common.ResolvedReason": "Duplicate"
60+
},
5461
"comment": "This is my comment. {{ report.input_sha256 }} {{ input_url }} <br> <pre>{{ repro_cmd }}</pre>",
5562
"unique_fields": ["System.Title", "System.AreaPath"],
5663
"on_duplicate": {

src/ApiService/ApiService/OneFuzzTypes/Model.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ public record AdoTemplate(
689689
List<string> UniqueFields,
690690
Dictionary<string, string> AdoFields,
691691
ADODuplicateTemplate OnDuplicate,
692+
Dictionary<string, string>? AdoDuplicateFields = null,
692693
string? Comment = null
693694
) : NotificationTemplate {
694695
public async Task<OneFuzzResultVoid> Validate() {
@@ -704,8 +705,9 @@ public record RenderedAdoTemplate(
704705
List<string> UniqueFields,
705706
Dictionary<string, string> AdoFields,
706707
ADODuplicateTemplate OnDuplicate,
708+
Dictionary<string, string>? AdoDuplicateFields = null,
707709
string? Comment = null
708-
) : AdoTemplate(BaseUrl, AuthToken, Project, Type, UniqueFields, AdoFields, OnDuplicate, Comment);
710+
) : AdoTemplate(BaseUrl, AuthToken, Project, Type, UniqueFields, AdoFields, OnDuplicate, AdoDuplicateFields, Comment);
709711

710712
public record TeamsTemplate(SecretData<string> Url) : NotificationTemplate {
711713
public Task<OneFuzzResultVoid> Validate() {

src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private static async Async.Task ProcessNotification(IOnefuzzContext context, Con
239239

240240
var renderedConfig = RenderAdoTemplate(logTracer, renderer, config, instanceUrl);
241241
var ado = new AdoConnector(renderedConfig, project!, client, instanceUrl, logTracer, await GetValidFields(client, project));
242-
await ado.Process(notificationInfo);
242+
await ado.Process(notificationInfo, config.AdoDuplicateFields);
243243
}
244244

245245
public static RenderedAdoTemplate RenderAdoTemplate(ILogger logTracer, Renderer renderer, AdoTemplate original, Uri instanceUrl) {
@@ -291,6 +291,7 @@ public static RenderedAdoTemplate RenderAdoTemplate(ILogger logTracer, Renderer
291291
original.UniqueFields,
292292
adoFields,
293293
onDuplicate,
294+
original.AdoDuplicateFields,
294295
original.Comment != null ? Render(renderer, original.Comment, instanceUrl, logTracer) : null
295296
);
296297
}
@@ -525,7 +526,7 @@ private async Async.Task<WorkItem> CreateNew() {
525526
return (taskType, document);
526527
}
527528

528-
public async Async.Task Process(IList<(string, string)> notificationInfo) {
529+
public async Async.Task Process(IList<(string, string)> notificationInfo, Dictionary<string, string>? duplicateFields) {
529530
var updated = false;
530531
WorkItem? oldestWorkItem = null;
531532
await foreach (var workItem in ExistingWorkItems(notificationInfo)) {
@@ -535,7 +536,7 @@ public async Async.Task Process(IList<(string, string)> notificationInfo) {
535536
_logTracer.AddTags(new List<(string, string)> { ("MatchingWorkItemIds", $"{workItem.Id}") });
536537
_logTracer.LogInformation("Found matching work item");
537538
}
538-
if (IsADODuplicateWorkItem(workItem)) {
539+
if (IsADODuplicateWorkItem(workItem, duplicateFields)) {
539540
continue;
540541
}
541542

@@ -575,13 +576,17 @@ public async Async.Task Process(IList<(string, string)> notificationInfo) {
575576
}
576577
}
577578

578-
private static bool IsADODuplicateWorkItem(WorkItem wi) {
579+
private static bool IsADODuplicateWorkItem(WorkItem wi, Dictionary<string, string>? duplicateFields) {
579580
// A work item could have System.State == Resolve && System.Reason == Duplicate
580581
// OR it could have System.State == Closed && System.Reason == Duplicate
581582
// I haven't found any other combinations where System.Reason could be duplicate but just to be safe
582583
// we're explicitly _not_ checking the state of the work item to determine if it's duplicate
583584
return wi.Fields.ContainsKey("System.Reason") && string.Equals(wi.Fields["System.Reason"].ToString(), "Duplicate", StringComparison.OrdinalIgnoreCase)
584585
|| wi.Fields.ContainsKey("Microsoft.VSTS.Common.ResolvedReason") && string.Equals(wi.Fields["Microsoft.VSTS.Common.ResolvedReason"].ToString(), "Duplicate", StringComparison.OrdinalIgnoreCase)
586+
|| duplicateFields?.Any(fieldPair => {
587+
var (field, value) = fieldPair;
588+
return wi.Fields.ContainsKey(field) && string.Equals(wi.Fields[field].ToString(), value, StringComparison.OrdinalIgnoreCase);
589+
}) == true
585590
// Alternatively, the work item can also specify a 'relation' to another work item.
586591
// This is typically used to create parent/child relationships between work items but can also
587592
// Be used to mark duplicates so we should check this as well.

src/ApiService/IntegrationTests/JinjaToScribanMigrationTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public async Async.Task OptionalFieldsAreSupported() {
111111
},
112112
"{{ if org }} blah {{ end }}"
113113
),
114+
null,
114115
"{{ if org }} blah {{ end }}"
115116
);
116117

@@ -137,6 +138,7 @@ public async Async.Task All_ADO_Fields_Are_Migrated() {
137138
},
138139
"{% if org %} comment {% endif %}"
139140
),
141+
null,
140142
"{% if org %} comment {% endif %}"
141143
);
142144

src/ApiService/Tests/OrmModelsTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ from authToken in Arb.Generate<SecretData<string>>()
232232
from str in Arb.Generate<NonEmptyString>()
233233
from fields in Arb.Generate<List<string>>()
234234
from adoFields in Arb.Generate<Dictionary<string, string>>()
235+
from adoDuplicateFields in Arb.Generate<Dictionary<string, string>>()
235236
from dupeTemplate in Arb.Generate<ADODuplicateTemplate>()
236237
select new AdoTemplate(
237238
baseUrl,
@@ -241,6 +242,7 @@ from dupeTemplate in Arb.Generate<ADODuplicateTemplate>()
241242
fields,
242243
adoFields,
243244
dupeTemplate,
245+
adoDuplicateFields,
244246
str.Get));
245247

246248
public static Arbitrary<TeamsTemplate> ArbTeamsTemplate()

src/pytypes/onefuzztypes/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ class ADOTemplate(BaseModel):
273273
unique_fields: List[str]
274274
comment: Optional[str]
275275
ado_fields: Dict[str, str]
276+
ado_duplicate_fields: Optional[Dict[str, str]]
276277
on_duplicate: ADODuplicateTemplate
277278

278279
# validator needed to convert auth_token to SecretData

0 commit comments

Comments
 (0)