TFS 2015.2 中的工作项创建问题(使用 vsts-node-api)

Work item creation issue in TFS 2015.2 (with vsts-node-api)

通过 vso-node-api 程序包在 TFS 2015.2(本地)中创建错误项时出现以下错误,但是相同的代码在我的 VSTS(在线)订阅中运行良好。

Error: Failed Request: Bad Request(400) - TF401349: An unexpected error has occurred, please verify your request and try again.
Status Code: 400

我正在使用 vso-node-api 库中的 WorkItemTrackingApi/createWorkItem 函数来创建工作项。以下是我用来创建工作项的示例代码。

WorkItemCreator.ts

import * as vm from 'vso-node-api/WebApi';
import * as wa from 'vso-node-api/WorkItemTrackingApi';
import * as wi from 'vso-node-api/interfaces/WorkItemTrackingInterfaces';
import * as vss from 'vso-node-api/interfaces/Common/VSSInterfaces';
import * as core from 'vso-node-api/interfaces/CoreInterfaces';
import tl = require('vsts-task-lib/task');

export class WorkItemCreator {
    workItemType: string = "Bug";
    fieldsToRetrieve: string[] = ["System.State", "System.Title"];

    collectionUrl: string;
    projName: string;
    accessToken: string;
    vstsWI: wa.IWorkItemTrackingApi;
    projectId: string;

    constructor() {
        console.log("Initializing Workitem Creator...");
        console.log("Retrieving enviornment values...");
        this.collectionUrl = process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"];
        this.projName = process.env["SYSTEM_TEAMPROJECT"];
        this.projectId = process.env["SYSTEM_TEAMPROJECTID"];

        console.log("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: " + this.collectionUrl);
        console.log("SYSTEM_TEAMPROJECT: " + this.projName);

        this.accessToken = this.getAccessToken();
        let creds = vm.getBearerHandler(this.accessToken);
        let connection = new vm.WebApi(this.collectionUrl, creds);
        this.vstsWI = connection.getWorkItemTrackingApi();
    }

    /**
     * Create Work Items
     */
    public createWorkItems(workItems: Array<BugItem>) {
        workItems.forEach(workItem => {
            let selectWorkItemsQry = { query: "Select [System.Id] From WorkItems Where [System.WorkItemType] = '" + this.workItemType + "' AND [System.Title] = '" + workItem.title + "'" };
            this.getWorkitem(this.projName, this.projectId, selectWorkItemsQry).then((qr: wi.WorkItemQueryResult) => {
                console.log("WorkItem Count:" + qr.workItems.length);
                if (qr.workItems.length == 0) {
                    console.log("Creating WorkItem '" + workItem.title + "' in project '" + this.projName + "'");
                    let xs: string[] = ["TagX", "TagY", "TagZ"];
                    console.log("Tags: " + xs);
                    this.createWorkitem(this.projName, this.workItemType, workItem.title, workItem.description, workItem.severity, xs);
                }
            })
            .catch((e) => {
                    console.error("Failed to retrieve WorkItem by title '" + workItem.title + "' Error: " + e);
            });
        });        
    }

    //Get acces token
    private getAccessToken(): string {
        tl.debug("Getting credentials for local feeds");
        let auth = tl.getEndpointAuthorization("SYSTEMVSSCONNECTION", false);
        if (auth.scheme === "OAuth") {
            console.log("Token retrieved: " + auth.parameters["AccessToken"]);
            tl.debug("Token retrieved: " + auth.parameters["AccessToken"]);
            return auth.parameters["AccessToken"];
        }
        else {
            tl.warning("Could not retrieve authentication token for Workitem creation.");
        }
    }


    //Create Workitem
    private createWorkitem(projectName: string, witype: string, title: string, description: string, severity: string, tagsCollection: string[]) {
        let wijson: vss.JsonPatchDocument = [
            { "op": "add", "path": "/fields/System.Title", "value": title },
        ];

        this.vstsWI.createWorkItem(null, wijson, projectName, witype, null, null).then((workitem: wi.WorkItem) => {
            console.log("WorkItem '" + workitem.id + "' Created");
        }).catch((e) => {
            console.error("Failed to create work item for '" + title + "' Error: " + e);
        });
    }

    /**
     * Get Workitems
     */
    private getWorkitem(projectName: string, teamProjectId: string, wiqlQuery: wi.Wiql): Promise<wi.WorkItemQueryResult> {
        console.log(wiqlQuery.query);
        let teamContext: core.TeamContext = { project: projectName, projectId: teamProjectId, team: "", teamId: "" };
        return this.vstsWI.queryByWiql(wiqlQuery, teamContext, null, null);
    }
}

/**
 * BugItem
 */
export class BugItem {
    title: string;
    description: string;
    severity: string;

    constructor(title: string, description: string, severity: string) {
        this.title = title;
        this.description = description;
        this.severity = severity;
    }
}

App.ts

/// <reference path="../definitions/node.d.ts" />
/// <reference path="../definitions/minimatch.d.ts" />
const Critical = "1 - Critical";
const Low = "4 - Low";

import * as wIc from './WorkItemCreator';

var x = new wIc.WorkItemCreator();
var fullCollection = new Array<wIc.BugItem>();

var itm1 = new wIc.BugItem("Bug1TitleItem1", "DescriptionCritical", Critical);
fullCollection.push(itm1);
var itm6 = new wIc.BugItem("Bug1TitleItem6","DescriptionLow",Low);
fullCollection.push(itm6);

x.createWorkItems(fullCollection);
console.log("End.");

范围: “范围”:[ "vso.build_execute", “vso.work_write” ],

正如这个 post 所指出的,本地 TFS 仅支持 NTLM 和 Kerberos 进行身份验证。我假设在 MS 更新 TFS 之前,访问令牌永远不是一个选项。

TFS 2015.3 不支持基于令牌的身份验证。替代方法是使用基本身份验证选项。如果扩展应该与 VSTS 和 TFS 兼容,那么您必须创建一种机制来为每个部署方案使用单独的身份验证机制。 TFS 2017 支持令牌身份验证,因此它可以像 VSTS 一样与 PAT 一起正常工作。