Create task from template via API in Microsoft Team Foundation Server 2018 - c#

I'm creating an integration from our testing framework (Selenium) to Team Foundation Server (TFS) 2018 using C# (our tests are already written in it) - the integration will generate work items in TFS based on test results. I'd like to create the work item from a template, and I can't seem to find any documentation on doing so. I'm using the TFS client library from Microsoft found here.
I can pull a template from TFS:
var client = new HttpClient()
// Client auth stuff removed
var method = new HttpMethod("GET");
var httpRequestMessage = new HttpRequestMessage(method, "http://server:port/tfs/collection/team/project/_api/wit/templates/12345abc");
var httpResponseMessage = client.SendAsync(httpRequestMessage).Result;
WorkItemTemplate tfs_template = httpResponseMessage.Content.ReadAsAsync<WorkItemTemplate>().Result;
The API for creating new work items here looks fairly straightforward, but I can't find any way to connect the two actions, or a way to apply a template using this call. Is it possible to create a work item via API with a template, and if so, is there any documentation for it?

You can not create work item from work item template. You can use work item templates to see which fields contains some default values. This example for new work item from template with rest api:
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.TeamFoundation.Core.WebApi.Types;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static string ServiceURL = "https://your_name.visualstudio.com";
static string PAT = "your pat";
static void Main(string[] args)
{
string projectName = "your project";
string templateName = "Critical bug";
/*connect to service. I use VSTS. In your case:
VssConnection connection = new VssConnection(new Uri(ServiceURL), new VssCredential());
https://learn.microsoft.com/ru-ru/azure/devops/integrate/get-started/client-libraries/samples?view=vsts
*/
VssConnection connection = new VssConnection(new Uri(ServiceURL), new VssBasicCredential(string.Empty, PAT));
//get clients
var WitClient = connection.GetClient<WorkItemTrackingHttpClient>();
var ProjectClient = connection.GetClient<ProjectHttpClient>();
var project = ProjectClient.GetProject(projectName).Result;
//get context for default project team
TeamContext tmcntx = new TeamContext(project.Id, project.DefaultTeam.Id);
//get all template for team
var templates = WitClient.GetTemplatesAsync(tmcntx).Result;
//get tempate through its name
var id = (from tm in templates where tm.Name == templateName select tm.Id).FirstOrDefault();
if (id != null)
{
var template = WitClient.GetTemplateAsync(tmcntx, id).Result;
JsonPatchDocument patchDocument = new JsonPatchDocument();
foreach (var key in template.Fields.Keys) //set default fields from template
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/" + key,
Value = template.Fields[key]
});
//add any additional fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = "My critical bug"
});
var newWorkItem = WitClient.CreateWorkItemAsync(patchDocument, projectName, template.WorkItemTypeName).Result;
}
}
}
}
My template:

Related

Programmatically retrieve list of source folders and create one if not present - C# + VSTS 2017

I am trying to automate some processes to make life a bit easier. We have multiple requests from the team to create a folder in TFS 2017 (they do not have permissions) and then set up the associated builds for that source control folder.
The build creation part I think I have a way to do, but querying our on premise TFS 2017 server to get a list of folders under a certain path is proving tricky. So far I am having trouble even connecting to the server in the first place with this :
var collectionUri = "http://tfs-server:8080/tfs/DefaultCollection/";
var teamProjectName = "MYPROJECT";
Uri uri = new Uri(collectionUri);
var clientCredentials = new VssCredentials(new WindowsCredential(new NetworkCredential("USERNAME", "PASSWORD", "COLLECTIONNAME")));
var connection = new VssConnection(uri, clientCredentials);
var sourceControlServer = connection.GetClient<TfvcHttpClient>();
That throws an exception : Error converting value "System.Security.Principal.WindowsIdentity;" to type 'Microsoft.VisualStudio.Services.Identity.IdentityDescriptor'
Can someone help me to get connected to the server first please! Documentation on this is very hard to find, and I dont see any examples that actually work.
What I was going to look at next was creating the folder if it doesn't exist. No idea how to do that yet, maybe using
sourceControlServer.GetBranchAsync(teamProjectName + FolderName);
Thanks!
EDIT:
Ok I got it to not error creating the connection by doing this instead :
Uri uri = new Uri("http://tfs-server:8080/tfs/DefaultCollection/");
var clientCredentials = new VssCredentials(new WindowsCredential(new NetworkCredential("USERNAME", "PASSWORD", "DOMAIN")));
var buildServer = new BuildHttpClient(uri, clientCredentials);
var sourceControlServer = new TfvcHttpClient(uri, clientCredentials);
So now to just figure out how to list and create folders from TFS and to create builds!
EDIT:
So I have got the querying working, so I can check if a folder exists under a path like this :
var teamProjectName = "USA";
Uri uri = new Uri("http://tfs-server:8080/tfs/DefaultCollection/");
var clientCredentials = new VssCredentials(new WindowsCredential(new NetworkCredential("USERNAME", "PASSWORD", "DOMAIN")));
TfvcHttpClient sourceControlServer = new TfvcHttpClient(uri, clientCredentials);
List<TfvcItem> branchItems;
using (sourceControlServer) {
branchItems = sourceControlServer.GetItemsAsync("$/USA/Development/NewFolder", VersionControlRecursionType.OneLevel).Result;
}
return branchItems.Count > 0;
That will find all the items under that folder. So if there isnt a folder, it will return 0, so I can go ahead and create that folder.
So next problem, is how to create the folder. Using CreateChangesetAsync.
Update:
To use Client API and CreateChangesetAsync method to create files in TFVC, you could refer below sample console app:
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
internal class Program
{
internal static async Task Main(string[] args)
{
var orgUrl = new Uri(args[0]);
string serverPath = args[1];
string localPath = args[2];
string contentType = args[3];
string pat = args[4];
var changes = new List<TfvcChange>()
{
new TfvcChange()
{
ChangeType = VersionControlChangeType.Add,
Item = new TfvcItem()
{
Path = serverPath,
ContentMetadata = new FileContentMetadata()
{
Encoding = Encoding.UTF8.WindowsCodePage,
ContentType = contentType,
}
},
NewContent = new ItemContent()
{
Content = Convert.ToBase64String(File.ReadAllBytes(localPath)),
ContentType = ItemContentType.Base64Encoded
}
}
};
var changeset = new TfvcChangeset()
{
Changes = changes,
Comment = $"Added {serverPath} from {localPath}"
};
var connection = new VssConnection(orgUrl, new VssBasicCredential(string.Empty, pat));
var tfvcClient = connection.GetClient<TfvcHttpClient>();
await tfvcClient.CreateChangesetAsync(changeset);
}
}
}
Besides, instead of using tf command line, please kindly check solution here:
C# TFS API: show project structure with folders and files, including their ChangeType (checked out, deleted,renamed) like in visual studio

Getting files/folders from sharepoint via the api in c#

I'm trying to link a c# application to a sharepoint directory, so I can create folders, download and upload files. However I am strugling with connecting to the correct folder.
I can retrieve the content from allitems.aspx, but I am not sure how to actually get the content from folder.
I have tried using the ClientContext - something like this:
ClientContext cxt = new ClientContext("https://xx.sharepoint.com/sites/");
cxt.Credentials = GetCredentials();
List list = cxt.Web.Lists.GetByTitle("Kontrakter");
var test = list.Views;
var test1 = cxt.Web.Lists;
cxt.Load(test1);
cxt.Load(list);
cxt.Load(test);
var a = 4;
var fullUri = new Uri("https://xx.sharepoint.com/sites/yy/Kontrakter/AllItems.aspx");
//var folder = cxt.Web.GetFolderByServerRelativeUrl(fullUri.AbsolutePath);
using (var rootCtx = new ClientContext(fullUri.GetLeftPart(UriPartial.Authority)))
{
rootCtx.Credentials = GetCredentials();
Uri webUri = Web.WebUrlFromPageUrlDirect(rootCtx, fullUri);
using (var ctx1 = new ClientContext(webUri))
{
ctx1.Credentials = GetCredentials();
var list1 = ctx1.Web.GetList(fullUri.AbsolutePath);
ctx1.Load(list1.RootFolder.Files);
ctx1.ExecuteQuery();
Console.WriteLine(list.RootFolder.Files.Count);
}
}
or via normal api calls like this:
https://xx.sharepoint.com/_api/Web/GetFolderByServerRelativeUrl('Kontrakter/Forms')/Files
The only way I can find some data is if I look into 'Shared documents/Forms'
I'm having problems understanding the directory structure and how I can actually find the content of files/folders.
Thanks in advance :)
Turned out I was missing a /sites in one of my uris.

SSAS automation in c# - rename AAS attribute in c# pragmatically

I need help on SSAS automation in C#. I want to automate the rename the display name for the AAS attributes.
To automate the full process of renaming using c#. please help on this. kindly share any links or article on this
The MS Documentation for AMO is here
Add the AMO nuget packge to your Visual Studio project
Then, you can connect to your SSAS Server from your code and change the dimension attribute.
E.g.,
private static void UpdateAttribute()
{
var serverName = ConfigurationManager.AppSettings["ServerName"];
var databaseName = ConfigurationManager.AppSettings["DatabaseName"];
var cubeName = ConfigurationManager.AppSettings["CubeName"];
var dimensionName = ConfigurationManager.AppSettings["DimensionName"];
var attributeName = ConfigurationManager.AppSettings["AttributeName"];
//Server
var server = new Server();
server.Connect(serverName);
//Database
var db = server.Databases.FindByName(databaseName);
//Cube
var cube = db.Cubes.FindByName(cubeName);
//dim
var dim = db.Dimensions.FindByName(dimensionName);
//attribute
var attribute = dim.Attributes.FindByName(attributeName);
var newAttributeName = $"{attribute.Name}_New";
attribute.Name = newAttributeName;
//this will update the dimension in the Server
dim.Update();
}
Result
The original attribute name was "Category" and now it is changed to "Category_New" in the Product dimension

Acumatica Prepare Replenishment Screen ProcessAll Action not working

I am on Acumatica 5.30.1672 and am using the SOAP API for Screen IN508000 to try and run the Prepare Replenishment process. I am setting the WarehouseID for where I want to prepare replenishment. The API call is returning the correct InventoryIDs in the response object but when I tell it to ProcessAll, it is not. There is no error, just no processing. I feel like I am missing something trivial here but I just cannot see it. When I do this using the GUI, everything works perfectly.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AcumaticaTest.AcumaticaWebReference;
namespace AcumaticaTest
{
class Program
{
static void Main(string[] args)
{
Screen context = new Screen();
context.CookieContainer = new System.Net.CookieContainer();
context.Timeout = 1200000;
context.Url = "url to SOAP API Endpoint";
LoginResult lresult = context.Login("<username>", "<password>");
IN508000Content IN508000 = context.IN508000GetSchema();
context.IN508000Clear();
var commands = new Command[]
{
new Value {Value = "<WarehouseID>", LinkedCommand = IN508000.Selection.Warehouse, Commit= true },
new Value {Value = "false", LinkedCommand = IN508000.Selection.Me, Commit= true },
IN508000.ItemsRequiringReplenishment.InventoryID,
IN508000.Actions.ProcessAll
};
var response = context.IN508000Submit(commands);
var status = context.IN508000GetProcessStatus();
while (status.Status == ProcessStatus.InProcess)
{
status = context.IN508000GetProcessStatus();
}
}
}
}
I tested your code and it is working just fine - the system prepares the replenishment plans for the items of the requested warehouse, and the response object contains all the items that would be listed if you opened the Prepare Replenishment screen with the parameters you set. If you want to e-mail me URL and credentials to your site (gmichaud at acumatica) I can take a look to see what's wrong.
Please note that you don't need to explicitly set Commit = true for these two fields (the schema object already has them set to true). I would also recommend to add System.Threading.Thread.Sleep(1000) in your loop to avoid hammering the server with requests while it is processing.

authentication failed while connecting to tfs using tfs api [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
authentication failed while connecting to tfs using tfs api
I am facing a strange issue.I want to connect tfs server using tfs api programmitcally.
Even after giving proper authentcaion crediatials it is failing.But if I do it manually by typing tfs server name in browser its got connected.
code:
TeamFoundationServer tfs =
new TeamFoundationServer(new Uri("http://xx.xx.xx.xx:8080/tfs"),
new NetworkCredential(#"user", "pass", "domain"));
tfs.EnsureAuthenticated()
Please suggest.
You may want to use this alternative:
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using System;
using System.Collections.ObjectModel;
class Program
{
static void Main()
{
var tfsUri = new Uri("http://xx.xx.xx.xx:8080/tfs");
TfsConfigurationServer configurationServer =
TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
// Get the catalog of team project collections
ReadOnlyCollection<CatalogNode> collectionNodes = configurationServer.CatalogNode.QueryChildren(
new[] { CatalogResourceTypes.ProjectCollection },
false, CatalogQueryOptions.None);
// List the team project collections
foreach (CatalogNode collectionNode in collectionNodes)
{
// Use the InstanceId property to get the team project collection
Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
TfsTeamProjectCollection teamProjectCollection = configurationServer.GetTeamProjectCollection(collectionId);
// Print the name of the team project collection
Console.WriteLine("Collection: " + teamProjectCollection.Name);
// Get a catalog of team projects for the collection
ReadOnlyCollection<CatalogNode> projectNodes = collectionNode.QueryChildren(
new[] { CatalogResourceTypes.TeamProject },
false, CatalogQueryOptions.None);
// List the team projects in the collection
foreach (CatalogNode projectNode in projectNodes)
{
Console.WriteLine(" Team Project: " + projectNode.Resource.DisplayName);
}
}
}
}
You can do it a simpler way (assuming you have pass through auth):
var tfsCollection = new TfsTeamProjectCollection(new Uri("http://yourtfs:8080/tfs/"));
tfsCollection.Authenticate();
var workItemStore = new WorkItemStore(TfsCollection);

Categories