rfennell / AzurePipelines

A single Repo to contain the source for ALL my Azure DevOps Build/Release Extensions. The packages can be found in the Azure DevOps Marketplace
https://marketplace.visualstudio.com/search?term=fenn&target=VSTS&sortBy=Relevance
MIT License
344 stars 431 forks source link

Suppressing duplicate parent work items #890

Closed smholvoet closed 3 years ago

smholvoet commented 3 years ago

How can I suppress duplicate parent work items? Let's say I have 3 linked work items which all have the same parent work item:

image

It finds the same parent 3 times over, which results in:

image

My handlebars template looks like this:

#### 🐞 Bugs
{{#forEach workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
#{{../id}}  
{{/if}}
{{/with}}
{{/forEach}}

#### 🔨 Tasks
{{#forEach workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Task')}}
#{{../id}}  
{{/if}}
{{/with}}
{{/forEach}}

 #### 🔗 Parents 
{{#forEach this.workItems}}
{{#forEach this.relations}}
{{#if (contains this.attributes.name 'Parent')}}
{{#with (lookup_a_work_item ../../relatedWorkItems  this.url)}}
#{{this.id}}  
{{/with}}
{{/if}}
{{/forEach}} 
{{/forEach}}
rfennell commented 3 years ago

A few ideas....

  1. You could nest your foreach blocks so you see a drill down

Not really a solution to the problem you ask, but will make the duplications less confusing

  1. You could just loop across the {{#forEach this.relations}}, a WI only appears once in this list, so you look for ones that have children or even specific children as defined by the list {{#forEach this.workItems}} - this might need option 3

  2. Or you could write a custom Handlebars extension to keep a track of the parent WI listed so they are only shown one.

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days

smholvoet commented 3 years ago

@rfennell Thanks for your suggestions 💪

I managed to solve this by using the return_parents_only custom Handlebars extension.

This somehow seems to work as the function in itself checks for duplicate parent items:

...
if (!foundList.includes(parent)) {
  foundList.push(parent);
  ret += block.fn(parent);
}
...

We only link Tasks to our PR's, which will either have a Bug or a User Story as their respective parent WI... so the following template works just fine and gets rid of any duplicate parent WI's:

#### 🐞 Bugs
{{#forEach workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
#{{../id}}  
{{/if}}
{{/with}}
{{/forEach}}

#### 🔨 Tasks
{{#forEach workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Task')}}
#{{../id}}  
{{/if}}
{{/with}}
{{/forEach}}

#### 🔗 Parent user stories

{{#return_parents_only this.workItems this.relatedWorkItems}}
#{{this.id}}
{{/return_parents_only}} 

Result: image

luisen22 commented 3 years ago

Good approach, do you know the syntaxis for a release pipeline?

rfennell commented 3 years ago

See the wiki for details, but the quick answer is you can either pass in the extension code as an inline parameter

- task: XplatGenerateReleaseNotes@3
   inputs:
      outputfile: '$(Build.ArtifactStagingDirectory)\releasenotes.md'
      # all the other parameters required
      customHandlebarsExtensionCode: |
         module.exports = {foo() {
            return 'Returns foo';
         }};

or the custom extension can be passed as file

- task: XplatGenerateReleaseNotes@3
   inputs:
      outputfile: '$(Build.ArtifactStagingDirectory)\releasenotes.md'
      # all the other parameters required
      customHandlebarsExtensionFile: $(System.SourceDirectory)\customcode.js
luisen22 commented 3 years ago

See the wiki for details, but the quick answer is you can either pass in the extension code as an inline parameter

- task: XplatGenerateReleaseNotes@3
   inputs:
      outputfile: '$(Build.ArtifactStagingDirectory)\releasenotes.md'
      # all the other parameters required
      customHandlebarsExtensionCode: |
         module.exports = {foo() {
            return 'Returns foo';
         }};

or the custom extension can be passed as file

- task: XplatGenerateReleaseNotes@3
   inputs:
      outputfile: '$(Build.ArtifactStagingDirectory)\releasenotes.md'
      # all the other parameters required
      customHandlebarsExtensionFile: $(System.SourceDirectory)\customcode.js

Thank you

luisen22 commented 3 years ago

In case you need to use tags with work item parents

{{#return_parents_only this.workItems this.relatedWorkItems}} {{#if (contains (lookup this.fields 'System.Tags') 'Tag#1')}}

JaydeepUniverse commented 3 years ago

@smholvoet @rfennell I am following same way to segregate WI type wise as below

#### 🐞 Bugs
{{#forEach workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
#{{../id}}  
{{/if}}
{{/with}}
{{/forEach}}

However apart from only WI ID, I need to have other details as well like Assigned to, Tags, Parents in table format

I tried as below but AssignedTo is not working, so just want help here

#### 🐞 Bugs
| WI ID | Tags | Assigned To |
|--|--|--|
{{#forEach this.workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
| #{{../id}} | {{'System.Tags'}} | {{#with (lookup 'System.AssignedTo')}} {{displayName}} {{/with}} |
{{/if}}
{{/with}}
{{/forEach}}
{{'System.AssignedTo.displayName'}} -------- it worked but didnt get output
{{#with System.AssignedTo}} {{displayName}} {{/with}} -------- it worked but didnt get output
{{#with (lookup 'System.AssignedTo')}} {{displayName}} {{/with}} -------- didnt work
{{#with 'System.AssignedTo'}} {{displayName}} {{/with}} -------- it worked but didnt get output
{{System.AssignedTo}} {{displayName}} -------- it worked but didnt get output

And how can I use parent in same table?

#### 🐞 Bugs
| WI ID | Tags | Assigned To | Parent |
|--|--|--|--|
{{#forEach this.workItems}}
{{#with fields}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
| #{{../id}} | {{'System.Tags'}} | {{#with (lookup 'System.AssignedTo')}} {{displayName}} {{/with}} | {{#return_parents_only this.workItems this.relatedWorkItems}} #{{this.id}} {{/return_parents_only}}  |
{{/if}}
{{/with}}
{{/forEach}}

Please advise. Thanks.

rfennell commented 3 years ago

I have answered this here, but I would usually recommend opening a new issue or discussion for a new question

Your basic idea for a table is correct, it is just the loop mechanism that is wrong. I would not use the outer {{#with}} wrapper

#### 🐞 Bugs
| WI ID | Tags | Assigned To |
|--|--|--|
{{#forEach this.workItems}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
| #{{this.id}} |  {{lookup this.fields 'System.Tags'}} |  {{#with (lookup this.fields 'System.AssignedTo')}} {{displayName}} {{/with}} |
{{/if}}
{{/forEach}}

As to the parent lookup, it will be something like

#### 🐞 Bugs
| WI ID | Tags | Assigned To | Parent |
|--|--|--|--|
{{#forEach this.workItems}}
{{#if (eq (get 'System.WorkItemType' this) 'Bug')}}
| #{{this.id}} |  {{lookup this.fields 'System.Tags'}} |  {{#with (lookup this.fields 'System.AssignedTo')}} {{displayName}} {{/with}} |  {{#forEach this.relations}}
{{#if (contains this.attributes.name 'Parent')}}
{{#with (lookup_a_work_item ../../relatedWorkItems  this.url)}}
      - {{this.id}} - {{lookup this.fields 'System.Title'}}
      {{#forEach this.relations}}
      {{#if (contains this.attributes.name 'Parent')}}
      {{#with (lookup_a_work_item ../../../../relatedWorkItems  this.url)}}
         - {{this.id}} - {{lookup this.fields 'System.Title'}}
      {{/with}}
      {{/if}}
      {{/forEach}}
{{/with}}
{{/if}}
{{/forEach}}
  |
{{/if}}
{{/forEach}}