Pushing an update to VSTS via REST API - rest

I am struggling with pushing an update to an existing file in a VSTS Git repository.
The documentation https://learn.microsoft.com/en-us/rest/api/vsts/git/pushes/create#update_a_file shows what to do.
When I use GET instead of POST, I get all existing pushes. So basically the request is working. This is my URL https://mine.visualstudio.com/_apis/git/repositories/ad3d7615-6e4a-4988-bd3f-ca80d7ec9791/pushes?api-version=4.1-preview.
I am testing the request from a console application. The body is created from a dynamic that is serialized later. The filecontent is a string var x=1;.
dynamic body = new
{
refUpdates = new[] { new { name = "refs/heads/master", oldObjectId = branchObjectId } },
commits = new[] {
new {
comment = "Updated Configuration" ,
changes = new[] {
new {
changeType = "edit",
item = new { path = "/files/filename.js"},
newContent = new { content = filecontent, contentType = "rawtext" }
}
}
}
}
};
The request looks like this:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
byte[] login = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalaccesstoken));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(login));
var content = new StringContent(body, Encoding.UTF8, "application/json");
using (HttpResponseMessage response = client.PostAsync(url, content).Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
responseBodyAction.Invoke(responseBody);
}
}
It is not successful, as the following Exception is thrown:
System.Net.Http.HttpRequestException: 'Response status code does not
indicate success: 404 (Not Found).
The body is a valid JSON.
Some API calls need the url .vsrm.visualstudio.com instead of .visualstudio.com. But not in this case.
I am out of ideas. Did anybody successfully push an update to VSTS and can show me how to do it?

As mentioned in a comment I was able to add instead of edit.
The solution is to first check in a file and then send an update/edit.
If there is no existing file, an edit will fail with the Exception.

Related

Update wiki page of tfs by calling rest-api

I want to update an already existing wiki page of tfs by using this document:
https://learn.microsoft.com/de-de/rest/api/azure/devops/wiki/pages/create%20or%20update?view=azure-devops-rest-4.1
Creating a new wiki-page (with content) is no problem. That is working fine.
But I want to edit an existing one. The tfs-documentation says that the only difference in API call is to use an "If-Match"-header (see section Request Header).
Here I have 3 situations:
Using no "If-Match"-Header or an empty: Get a "412 Precondition Failed" error.
Using a "If-Match"-Header with random value: Get a "400 Bad Request" error.
Using a "If-Match"-Header with exactly 40 characters (like the version-hash of the page-revision (e.g. '09f62be600a3b6d36d21b294dbb00921a5ba03ec')): Again "412 Precondition Failed" error.
I think the revision-hash (40 characters) should be a good way because the error message on non-40-chars returns the 400-error.
But it did not work? Has anyone an idea which id tfs is wanting? I used Postman and C# to update by API. Below you can see my example code:
var handler = new HttpClientHandler()
{
UseDefaultCredentials = true,
UseProxy = false,
};
var client = new HttpClient(handler);
client.BaseAddress = new Uri(".../pages/pagename" + "?api-version=4.1");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var putContent = new StringContent("{ \"content\": \"New content for page\" }", Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("If-Match", "09f62be600a3b6d36d21b294dbb00921a5ba03ec");
var responseTask = client.PutAsync(client.BaseAddress, putContent);
var result = responseTask.Result;
var content = result.Content.ReadAsStringAsync().Result;
var code = result.StatusCode;
var body = content;
According to the Create or Update Wiki API, if we want to edit the wiki page,If-Match header is required. The value of If-Matchth is the wiki page ETag.
ETags can also be used for optimistic concurrency control, as a way to help prevent simultaneous updates of a resource from overwriting each other
so we need to get the wiki Etag before update. Please have a try to change the code as following:
var baseUrl = "xxxxx";
var handler = new HttpClientHandler()
{
UseDefaultCredentials = true,
UseProxy = false,
};
var client = new HttpClient(handler)
{
BaseAddress = new Uri(baseUrl)
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "YourToken");
var getResult = client.GetAsync(baseUrl).Result;
var etag = getResult.Headers.GetValues("ETag");
var putContent = new StringContent("{ \"content\": \"New content for page\" }", Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("If-Match", etag);
var responseTask = client.PutAsync(client.BaseAddress, putContent);
var result = responseTask.Result;
var content = result.Content.ReadAsStringAsync().Result;
Test Result:

How create bug work item in visual studio online from wpf app or some other web app?

I have a bugs list managed in a WPF app. I would like to create the bug in the VSO work item. Is there an API available to create work items like bug or task in Visual Studio Online?
Yes, there has the REST API to create a work item. The example code as:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
...
public void CreateBug()
{
string _personalAccessToken = "your personal access token";
string _credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", _personalAccessToken)));
Object[] patchDocument = new Object[4];
patchDocument[0] = new { op = "add", path = "/fields/System.Title", value = "Authorization Errors" };
patchDocument[1] = new { op = "add", path = "/fields/Microsoft.VSTS.TCM.ReproSteps", value = "Our authorization logic needs to allow for users with Microsoft accounts (formerly Live Ids) - http://msdn.microsoft.com/en-us/library/live/hh826547.aspx" };
patchDocument[2] = new { op = "add", path = "/fields/Microsoft.VSTS.Common.Priority", value = "1" };
patchDocument[3] = new { op = "add", path = "/fields/Microsoft.VSTS.Common.Severity", value = "2 - High" };
//use the httpclient
using (var client = new HttpClient())
{
//set our headers
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
//serialize the fields array into a json string
var patchValue = new StringContent(JsonConvert.SerializeObject(patchDocument), Encoding.UTF8, "application/json-patch+json");
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, "https://accountname.visualstudio.com/fabrikam/_apis/wit/workitems/$Bug?api-version=2.2") { Content = patchValue };
var response = client.SendAsync(request).Result;
//if the response is successfull, set the result to the workitem object
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
}
}
More details, you can refer create bug.

Enhanced Security Error while Visual Studio Team Services Rest API

I'm currently trying to use the Rest APIs exposed by Visual Studio Team Services (was Visual Studio Online) to obtain work item information. I seem to be able to connect however when I look at the response to my query its a html page with a Enhanced Security Error message. I believe that this is due to the Enhanced Security option in IE but I'm calling this from my client machine and I can only see options on how to turn this off on a server.
this is the call i'm making
using (var client = new HttpClient())
{
var token = "xxxxxxxxxxxx";
var apiVersion = "1.0";
var account = "xxxxxxxx";
var query = "Select [System.Id] From WorkItems Where[System.WorkItemType] = 'WorkItem' order by [System.CreatedDate] desc";
var url = "https://" + account + ".visualstudio.com/Core/_apis/wit/";
// Execute a query that returns work item IDs matching the specified criteria
using (var request = new HttpRequestMessage(HttpMethod.Post, url + "wiql"))
{
request.Headers.Add("Authorization", "Bearer " + token);
request.Headers.Add("Accept", "application/json;api-version=" + apiVersion);
Dictionary<string, string> body = new Dictionary<string, string>
{
{
"query", query
}
};
request.Content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
using (var response = await client.SendAsync(request))
{
var content = await response.Content.ReadAsStringAsync();
var workItems = JObject.Parse(content)["workItems"] as JArray;
string[] ids = workItems.Select<JToken, string>(w => (w["id"] + "")).Take(10).ToArray<string>();
string idsString = String.Join(",", ids);
// Get details for the last 10
using (var detailsRequest = new HttpRequestMessage(HttpMethod.Get, url + "workitems?ids=" + idsString + "&fields=System.Id,System.Title"))
{
detailsRequest.Headers.Add("Authorization", "Bearer " + token);
detailsRequest.Headers.Add("Accept", "application/json;api-version=" + apiVersion);
using (var detailsResponse = await client.SendAsync(detailsRequest))
{
var detailsContent = await detailsResponse.Content.ReadAsStringAsync();
var detailsWorkItems = JObject.Parse(detailsContent)["value"] as JArray;
foreach (dynamic workItem in detailsWorkItems)
{
Console.WriteLine("Work item: {0} ({1})",
workItem.fields["System.Id"],
workItem.fields["System.Title"]
);
}
}
}
}
}
}
any help with this would be appreciated,
thanks
Chris
You can add related sites to trusted sites (for example: https://app.vssps.visualstudio.com, https://login.live.com etc…).
Internet option=>Security
Select Trusted sites
Click sites
Type website address and click add
The simple way to know which URLs need to be added, you could send a simple Get Rest request (e.g. get work item REST API), it will pop up a window that contains site URL (will pop up many times for different URL), add these URL to trusted sites list.
Update:
Based on the response result, it isn’t related to enhanced security, the result means it isn’t authenticated. So the token is invalid, it is access token of OAuth, you need to get access token after register your app to VSTS.
More information, you can refer to this article.
There is a OAuth sample that you can refer. After you get access token, add it to request header and retrieve data from VSTS.
If you want to access VSTS through personal access token, the code like this: (check this article)
try
{
var username = "username";
var password = "password";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", username, password))));
using (HttpResponseMessage response = client.GetAsync(
"https://{account}.visualstudio.com/DefaultCollection/_apis/build/builds").Result)
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

making a REST calls from unity3d - Need to pass JSON object using HTTPWebRequest

How to make a REST call in unity3d? especially POST method. I have tried with GET request. Pls see the below get request. so i need to write POST request in Unity3d. The post rquest should be in JSON format. I tried with below code. It's hit the my service but the receiving JSON object is null. Hope your support.
var httpWebReq = WebRequest.Create("http://localhost:6091/UserService.svc/RegisterUser/") as HttpWebRequest;
httpWebReq.ContentType = "text/json;charset=utf-8";
httpWebReq.Method= "POST";
using(var streamWriter = new StreamWriter(httpWebReq.GetRequestStream()))
{
string user = "{UserID:0," +
"Email:'ruwan#gmail.com'," +
"Password:'ruwan123'," +
"NickName:'ruwa'," +
"Age:35" +
"}";
byte[] formData = UTF8Encoding.UTF8.GetBytes(user);
httpWebReq.ContentLength = formData.Length;
streamWriter.Write(formData);
}
var httpResponse = (HttpWebResponse)httpWebReq.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
//Now you have your response.
//or false depending on information in the response
Debug.Log(responseText);
}
use WWW for GET,
use WWW with WWWForm for POST.
Finally i got the solution thanks to all for help.
Actually the easiest way to make rest call for WCF is we have to add the Newtonsoft.Json. Finally my code is-
GET --
WebClient myWebClient = new WebClient();
myWebClient.Encoding = Encoding.UTF8;
myWebClient.Headers.Add("Content-Type", "text/json");
var json = JsonConvert.DeserializeObject<Room[] >(new WebClient().DownloadString("Your URL"));
List<yourclass> test1= new List<yourclass>();
foreach (var test in json)
{
test1.Add(new yourclass()
{
yourclass.property1 = test.property1
});
}
Debug.Log(test1);
POST---
WebClient myWebClient = new WebClient();
var Test = JsonConvert.SerializeObject(new
{
YourProperty= 0
}, new JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.None });
myWebClient.Encoding = Encoding.UTF8;
myWebClient.Headers.Add("Content-Type", "text/json");
string responsebody = myWebClient.UploadString("Your URL", "POST", Test );
//if(responsebody == true)
Debug.Log(responsebody);

Working with Azure in Winrt with Rest API, trouble with signature

I'm trying to work with azure storage in winrt. Since the azure storage client is not compatible with winrt I am trying to use azure's rest API. I am having a heck of a time getting the signature right and I could use another set of eyes to help me see where I'm going wrong.
Azure Account provides a name and key property, this method builds up the request right now simply listing all blobs.
'private async void BuildHTTPRequest(AzureAccount account)
{
System.Net.Http.HttpClient request = new HttpClient();
request.BaseAddress = new Uri(string.Format("http://{0}.blob.core.windows.net/", account.Name));
// Always have to use UTC date/time
request.DefaultRequestHeaders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
string fmtStringToSign = "{0}\n{1}\n{2}\n{3:R}\n{4}{5}";
request.DefaultRequestHeaders.Add("x-ms-version", "2011-08-18");
string hdr = CanonicalizeHeaders(request.DefaultRequestHeaders);
string authValue = string.Format(fmtStringToSign, "GET", "", "", "", hdr, "");
byte[] signatureByteForm = System.Text.Encoding.UTF8.GetBytes(authValue);
string hashKey = account.Key;
MacAlgorithmProvider macAlgorithmProvider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA256");
BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
var messageBuffer = CryptographicBuffer.ConvertStringToBinary(authValue, encoding);
IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(hashKey, encoding);
CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);
string hashedString = CryptographicBuffer.EncodeToBase64String(signedMessage);
String authHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", "SharedKey",
account.Name, hashedString);
request.DefaultRequestHeaders.Add("Authorization", authHeader);
// Send the request to the queue
try
{
var test1 = request.GetAsync("?comp=list").Result;
if (test1.IsSuccessStatusCode)
{
}
}
catch (WebException ex) { }
}
This should set the headers up for signing...
public string CanonicalizeHeaders(System.Net.Http.Headers.HttpRequestHeaders hdrCollection)
{
StringBuilder retVal = new StringBuilder();// Look for header names that start with "x-ms-" // Then sort them in case-insensitive manner.
List<string> httpStorageHeaderNameArray = new List<string>();
Dictionary<string, string> ht = new Dictionary<string, string>();
foreach (var key in hdrCollection)
{
if (key.Key.ToLowerInvariant().StartsWith("x-ms-", StringComparison.Ordinal))
{
if (ht.ContainsKey(key.Key.ToLowerInvariant()))
{
ht[key.Key.ToLowerInvariant()] = string.Format("{0},{1}", ht[key.Key.ToLowerInvariant()],
hdrCollection.FirstOrDefault(m => m.Key == key.Key).ToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Trim());
}
else
{
httpStorageHeaderNameArray.Add(key.Key.ToLowerInvariant());
ht.Add(key.Key.ToLowerInvariant(),
hdrCollection.FirstOrDefault(m => m.Key == key.Key).Value.FirstOrDefault().ToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Trim());
}
}
}
httpStorageHeaderNameArray.Sort();// Now go through each header's values in the sorted order and append them to the canonicalized string.
foreach (string key in httpStorageHeaderNameArray)
{
retVal.AppendFormat("{0}:{1}\n", key.Trim(), ht[key]);
}
return retVal.ToString();
}
'
Latest version of storage client library supports WinRT. You can read more about it here: http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/introducing-windows-azure-storage-client-library-2-0-for-net-and-windows-runtime.aspx. What I did was download the source code from Github: https://github.com/WindowsAzure/azure-sdk-for-net, opened the solution in VS 2012 and built RT project to get the necessary winmd files.
Coming to your problem, I believe you're running into this issue because you're passing an empty string for canonicalized resource string:
string authValue = string.Format(fmtStringToSign, "GET", "", "", "", hdr, "")
Please see this link for more details on creating canonicalized resource string: http://msdn.microsoft.com/en-us/library/windowsazure/dd179428.aspx.