Search code examples
pythonazure-devopsazure-devops-rest-api

Can't create new work item type states through Azure DevOps REST API


Been writing a Python code to automate a few setups of the organization creation process in my company, but I can’t create new states in some of the work item types. And, in a odd manner, this seems to happen only in work item types that were already in the system. Here’s the full code:

import base64
from os import environ as env
from dotenv import load_dotenv
import requests
import time
import json
import pandas as pd
import contador

load_dotenv()

AZURE_DEVOPS_PAT = env['AZURE_DEVOPS_PAT']

patEncoded = base64.b64encode((":" + AZURE_DEVOPS_PAT).encode()).decode()

organization = 'dummy-name'

headers = {
  'Content-Type': 'application/json',
  'Authorization': f'Basic {patEncoded}'
}

def getProcessID(testName, organization, headers):
  global contador
  getUrl = f'https://dev.azure.com/{organization}/_apis/process/processes?api-version=7.1-preview.1'
  while True:
    try:
        req = requests.get(getUrl, headers=headers)
        break
    except requests.exceptions.ConnectTimeout as e:
      contador.contador += 1
      print(f"Erro de timeout número {contador.contador}. Tentando novamente...")
      time.sleep(10)
  json_object = json.loads(req.text)
  reqTextSize = len(json_object['value'])
  for i in range(reqTextSize):
    name = json_object['value'][i]['name']
    if name == testName:
      processId = json_object['value'][i]['id']
  return processId

def criarEstados(tipo, processId, organization, headers):
    tabelaRefNames = getWorkItemTypes(processId, organization, headers)
    filePath = "printDebug.txt"
    if tipo == 'Agile':
        tabelaEstados = pd.read_excel('kanban.xlsx')
    elif tipo == 'Scrum':
        tabelaEstados = pd.read_excel('scrum.xlsx')
    tabelaEstados = tabelaEstados.merge(tabelaRefNames, left_on="workItemType", right_on="originName", how='left')
    tabelaEstadosDict = tabelaEstados.to_dict()
    tabelaEstadosDictSize = len(tabelaEstadosDict["workItemType"])
    linhas = []
    with open(filePath, 'w') as printDebug:
            for i in range(tabelaEstadosDictSize):
                workItemType = tabelaEstadosDict["workItemType"][i]
                witRefName = tabelaEstadosDict["referenceName"][i]
                name = tabelaEstadosDict["originName"][i]
                color = tabelaEstadosDict["color"][i]
                stateCategory = tabelaEstadosDict["stateCategory"][i]
                referenceName = f"{witRefName} foi passado\n"
                status = criarEstado(processId, organization, headers, witRefName, name, color, stateCategory)
                resultState = f'Estado {name} no Work Item Type {workItemType} deu este retorno:\n'
                linhas.extend([resultState, referenceName, f'{status}\n'])
                printDebug.writelines(linhas)
                linhas.clear()
    print("Debug finalizado.")

def main():
  testName = "Processo-Kanban"
  processId = getProcessID(testName, organization, headers)
  criarEstados('Agile', processId, organization, headers)

main()
For Microsoft.VSTS.WorkItemTypes.Bug as witRefName:
{
  "$id": "1",
  "innerException": null,
  "message": "VS402805: Cannot find work item type with reference name 'Microsoft.VSTS.WorkItemTypes.Bug' in process named '07c52453-a09e-4f35-a0f1-40bdb972afb9'.",
  "typeName": "Microsoft.TeamFoundation.WorkItemTracking.Server.Metadata.ProcessWorkItemTypeDoesNotExistException, Microsoft.TeamFoundation.WorkItemTracking.Server",
  "typeKey": "ProcessWorkItemTypeDoesNotExistException",
  "errorCode": 0,
  "eventId": 3200
}
For Microsoft.VSTS.WorkItemTypes.UserStory as witRefName:
{
  "$id": "1",
  "innerException": null,
  "message": "VS402805: Cannot find work item type with reference name 'Microsoft.VSTS.WorkItemTypes.UserStory' in process named '07c52453-a09e-4f35-a0f1-40bdb972afb9'.",
  "typeName": "Microsoft.TeamFoundation.WorkItemTracking.Server.Metadata.ProcessWorkItemTypeDoesNotExistException, Microsoft.TeamFoundation.WorkItemTracking.Server",
  "typeKey": "ProcessWorkItemTypeDoesNotExistException",
  "errorCode": 0,
  "eventId": 3200
}
For Microsoft.VSTS.WorkItemTypes.Task  as witRefName:
{
  "$id": "1",
  "innerException": null,
  "message": "VS402805: Cannot find work item type with reference name 'Microsoft.VSTS.WorkItemTypes.Task' in process named '07c52453-a09e-4f35-a0f1-40bdb972afb9'.",
  "typeName": "Microsoft.TeamFoundation.WorkItemTracking.Server.Metadata.ProcessWorkItemTypeDoesNotExistException, Microsoft.TeamFoundation.WorkItemTracking.Server",
  "typeKey": "ProcessWorkItemTypeDoesNotExistException",
  "errorCode": 0,
  "eventId": 3200
}
For Microsoft.VSTS.WorkItemTypes.Epic as witRefName:
{
  "$id": "1",
  "innerException": null,
  "message": "VS402805: Cannot find work item type with reference name 'Microsoft.VSTS.WorkItemTypes.Epic' in process named '07c52453-a09e-4f35-a0f1-40bdb972afb9'.",
  "typeName": "Microsoft.TeamFoundation.WorkItemTracking.Server.Metadata.ProcessWorkItemTypeDoesNotExistException, Microsoft.TeamFoundation.WorkItemTracking.Server",
  "typeKey": "ProcessWorkItemTypeDoesNotExistException",
  "errorCode": 0,
  "eventId": 3200
}

I've succeeded with this script to create the work item state, but only for custom work item types. And also I'm able to create new work item type states through the web UI (organization settings in Azure Devops).

Also, once I added the states to the Bug work item type through the web UI, the code captured the witRefName as “Processo-Kanban.Bug”, and no longer as “Microsoft.VSTS.WorkItemTypes.Bug”.

The same happened to the Epic work item type. And, for both, my code seemed to start working as intended.

So is it possible that the witRefName should be something along the lines of f"{processName}.{workItemName)?

Anyway, could anyone please help me?

I've tried different ways to obtain the witRefName, and they all failed. Code's above.


Solution

  • It seemed that you were trying to add new state for the work item type in the default process. Kindly note that to customize the work tracking system, you need to customize an inherited process.

    Here is my work flow for your refence to create new state for the Bug in one of my inherited processes AgileInherited.

    1. Get(list) the processId of the target inherited process via API;

      GET https://dev.azure.com/{organization}/_apis/work/processes?api-version=7.2-preview.2
      

      Agile is one of the 4 default processes, while AgileInherited is the customized inherited process.

      …
              {
                  "typeId": "adcc42ab-9882-485e-a3ed-7678f01f66bc",
                  "name": "Agile",
                  "referenceName": null,
                  "description": "This template is flexible and will work great for most teams using Agile planning methods, including those practicing Scrum.",
                  "parentProcessTypeId": "00000000-0000-0000-0000-000000000000",
                  "isEnabled": true,
                  "isDefault": false,
                  "customizationType": "system"
              },
              {
                  "typeId": "f37a3e41-85fc-4cbd-8a14-5b97df442661",
                  "name": "AgileInherited",
                  "referenceName": "Inherited.f37a3e4185fc4cbd8a145b97df442661",
                  "description": "Inherited process from Agile",
                  "parentProcessTypeId": "adcc42ab-9882-485e-a3ed-7678f01f66bc",
                  "isEnabled": true,
                  "isDefault": true,
                  "customizationType": "inherited"
              },
      …
      
    2. Get(list) the referenceName of target work item type via API;

      GET https://dev.azure.com/{organization}/_apis/work/processes/{processId}/workitemtypes?api-version=7.2-preview.2
      

      Per the Bug work item type in my InheritedAgile process, its witRefName as well as referenceName is AgileInherited.Bug;

      …
              {
                  "referenceName": "AgileInherited.Bug",
                  "name": "Bug",
                  "description": "Describes a divergence between required and actual behavior, and tracks the work done to correct the defect and verify the correction.",
                  "url": "https://dev.azure.com/{organization}/_apis/work/processes/f37a3e41-85fc-4cbd-8a14-5b97df442661/workItemTypes/AgileInherited.Bug",
                  "customization": "inherited",
                  "color": "CC293D",
                  "icon": "icon_insect",
                  "isDisabled": false,
                  "inherits": "Microsoft.VSTS.WorkItemTypes.Bug"
              },
      …
      
    3. Create new state Ready to test for the Bug in my AgileInherited process via API;

      POST https://dev.azure.com/{organization}/_apis/work/processes/{processId}/workItemTypes/{witRefName}/states?api-version=7.2-preview.1
      

      My Sample

      POST https://dev.azure.com/{organization}/_apis/work/processdefinitions/f37a3e41-85fc-4cbd-8a14-5b97df442661/workItemTypes/AgileInherited.Bug/states?api-version=7.2-preview.1
      
      {
        "name": "Ready to test",
        "stateCategory": "InProgress",
        "color": "207752",
        "order": null
      }
      
    4. The new state is visible in Bug of AgileInherited process. enter image description here

    Hope the information could help resolve your concerns.