I'm trying to create a .NET web application integration with RTC, where I would insert new workitems using RTC change management services, as defined in this article (specifically in "Create a Change Request"). I was able to get the URL-to-be-used inside services.xml file (/oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/) and my goal is to insert data using JSON.
My code is basically the following:
CookieContainer cookies = new CookieContainer();
HttpWebRequest documentPost = (HttpWebRequest)WebRequest.Create(rtcServerUrl + "/oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/order");//"Order" is the workitem name
documentPost.Method = "POST";
documentPost.CookieContainer = cookies;
documentPost.Accept = "application/json";
documentPost.ContentType = "application/x-oslc-cm-change-request+json";
documentPost.Timeout = TIMEOUT_SERVICO;
string json = "{ \"dc:title\":\"" + title + "\", \"rtc_cm:filedAgainst\": [ { \"rdf:resource\" : \"" + rtcServerUrl + "/resource/itemOid/com.ibm.team.workitem.Category/" + idCategory + "\"} ] }"; //dc:title and rtc_cm:filedAgainst are the only two mandatory data from the workitem I'm trying to create
using (var writer = new StreamWriter(documentPost.GetRequestStream()))
{
writer.Write(json);
writer.Flush();
writer.Close();
}
Encoding encode = System.Text.Encoding.UTF8;
string retorno = null;
//Login
HttpWebRequest formPost = (HttpWebRequest)WebRequest.Create(rtcServerUrl + "/j_security_check");
formPost.Method = "POST";
formPost.Timeout = TIMEOUT_REQUEST;
formPost.CookieContainer = request.CookieContainer;
formPost.Accept = "text/xml";
formPost.ContentType = "application/x-www-form-urlencoded";
String authString = "j_username=" + userName + "&j_password=" + password; //create authentication string
Byte[] outBuffer = System.Text.Encoding.UTF8.GetBytes(authString); //store in byte buffer
formPost.ContentLength = outBuffer.Length;
System.IO.Stream str = formPost.GetRequestStream();
str.Write(outBuffer, 0, outBuffer.Length); //update form
str.Close();
//FormBasedAuth Step2:submit the login form and get the response from the server
HttpWebResponse formResponse = (HttpWebResponse)formPost.GetResponse();
var rtcAuthHeader = formResponse.Headers["X-com-ibm-team-repository-web- auth-msg"];
//check if authentication has failed
if ((rtcAuthHeader != null) && rtcAuthHeader.Equals("authfailed"))
{
//authentication failed. You can write code to handle the authentication failure.
//if (DEBUG) Console.WriteLine("Authentication Failure");
}
else
{
//login successful
HttpWebResponse responseRetorno = (HttpWebResponse)request.GetResponse();
if (responseRetorno.StatusCode != HttpStatusCode.OK)
retorno = responseRetorno.StatusDescription;
else
{
StreamReader reader = new StreamReader(responseRetorno.GetResponseStream());
retorno = "[Response] " + reader.ReadToEnd();
}
responseRetorno.Close();
formResponse.GetResponseStream().Flush();
formResponse.Close();
}
As I was managed to search for in other forums, this should be enough in order to create the workitem (I have a very similar code working to update workitems using "" URL and PUT method). However, instead of create the workitem in RTC and give me some response with item's identifier, the request's response returns a huge JSON file that ends with "oslc_cm:next":"https:///oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/%7B0%7D?oslc_cm.pageSize=50&_resultToken=_AAY50FEkEee1V4u7RUQSjA&_startIndex=50. It's a JSON representation of the XML I receive when I access /oslc/contexts/_0_iN4G09EeGGMqpyZT5XdQ/workitems/ directly from browser, like I was trying to do a simple query inside the workitem's collection (even though I'm using POST, not GET).
I also tried to use PUT method, but then I receive a 405 status code.
Does anyone have an idea of what am I missing here? My approach is wrong, even though with the same approach I'm able to update existing workitem data in RTC?
Thanks in advance.
Related
I have been searching for an answer to this question, but I keep coming up short so hopefully I can find an answer. Admittedly I am not the best C# programmer and this is born out of necessity and not having a resource to help develop this for me, so I have jumped in feet first.
I have some code that I have successfully posted JSON data to the API IF I hard code the JSON string, but I would like to set the results from a SQL query as an OBJ and then serialize them using NEWTONSOFT.JSON to pass to the API in place of the hard coded data.
public void Main()
{
string url = Dts.Variables["$Package::url"].Value.ToString();
string user = Dts.Variables["$Package::user"].Value.ToString();
string pwd = Dts.Variables["$Package::pwd"].GetSensitiveValue().ToString();
string result = Dts.Variables["User::JSON"].Value.ToString();
var JsonResult = JsonConvert.SerializeObject(result);
var request = (HttpWebRequest)WebRequest.Create(url);
string authHeader = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(user + ":" + pwd));
request.Headers.Add("Authorization", "Basic" + " " + authHeader);
request.ContentType = "application/json";
request.Accept = "application/json";
request.Method = "POST";
request.Headers.Add("Cookie: freedomIdentifyKey=XX");
result.ToString();
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(JsonResult);
streamWriter.Flush();
streamWriter.Close();
var httpResponse = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result2 = streamReader.ReadToEnd();
}
}
Dts.TaskResult = (int)ScriptResults.Success;
}
I keep getting Error: 0x1 at Script Task: Exception has been thrown by the target of an invocation.
Any thoughts on how I could resolve this?
You don't need to use a StreamWriter.
string url = Dts.Variables["$Package::url"].Value.ToString();
string user = Dts.Variables["$Package::user"].Value.ToString();
string pwd = Dts.Variables["$Package::pwd"].GetSensitiveValue().ToString();
string result = Dts.Variables["User::JSON"].Value.ToString();
var JsonResult = JsonConvert.SerializeObject(result);
var request = (HttpWebRequest)WebRequest.Create(url);
string authHeader = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(user + ":" + pwd));
request.Headers.Add("Authorization", "Basic" + " " + authHeader);
request.ContentType = "application/json";
request.Accept = "application/json";
request.Method = "POST";
request.Headers.Add("Cookie: freedomIdentifyKey=XX");
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(JsonResult);
using (var dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
}
}
And if you want to use HttpClient instead:
string url = Dts.Variables["$Package::url"].Value.ToString();
string user = Dts.Variables["$Package::user"].Value.ToString();
string pwd = Dts.Variables["$Package::pwd"].GetSensitiveValue().ToString();
string result = Dts.Variables["User::JSON"].Value.ToString();
// HttpClient is intended to be instantiated once per application, rather than per-use. See Remarks.
var client = new HttpClient();
// Call asynchronous network methods in a try/catch block to handle exceptions.
try
{
var response = await client.PostAsync(url, result);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
}
catch(HttpRequestException e)
{
// do something
}
Not tested
I was able to get this sorted out. The issue wasn't with the code, but rather with the Newtonsoft.JSON package. It was failing before it even got to the code and I was trying to run it as a SQL 2016 target. When I switched back to a 2019 project it ran fine. To get it to run as a 2016, I installed NEWTONSOFT.JSON using the GACUTIL and it runs great now.
I did make a change and used an SQL query in the code instead of using an execute SQL task and then passing it to a variable.
I am trying to integrate with a Payment gateway called PayFort, everything went okay and the method I that I used returns HTML code, which will be the page that a user should see to proceed in the payment process..
What I need is how to render that HTML response into the browser, I investigated about some solutions and all of them are using StreamReader and Writer I tried it by calling the Payment method URL directly by the browser and it worked perfectly, but when I tried to call it from JS/Ajax it didn't do any action, it didn't launch the HTML response.
Below is the code that I used to integrate with the Payment Gateway:
public string TryPayment(int ID)
{
var BaseURL = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"));
setConfig();
api_url = Command.GetAPIURL(Command.IntegrationTypes.Redirect, true);
package = Umbraco.Content(ID);
int price = Convert.ToInt32(package.Value("price"));
VALUE = price;
MyReference = ("MyReference" + (DateTime.Now).ToString()).Replace(" ", "").Replace(":", "").Replace("/", "");
createSignature(MyReference, VALUE);
var newdata = "command=PURCHASE" +
"&access_code=My Code" +
"&merchant_identifier=My Identifier" +
"&merchant_reference=" + MyReference +
"&customer_email=Name#email.com" +
"&amount=" + VALUE +
"¤cy=JOD&language=ar" +
"&return_url=" + BaseURL + "umbraco/surface/FortResponse/working" +
"&signature=" + signature;
byte[] dataBytes = Encoding.UTF8.GetBytes(newdata);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://sbcheckout.payfort.com/FortAPI/paymentPage");
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
request.ContentLength = dataBytes.Length;
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
using (Stream requestBody = request.GetRequestStream())
{
requestBody.Write(dataBytes, 0, dataBytes.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(response.CharacterSet)))
{
return reader.ReadToEnd();
}
}
Which works when I call it by the browser but doesn't when I call it by JS/Ajax.
Any insight would be appreciated.
Try below ways
1) Use javaScript
success: function(data) {
window.location.href="nextstep.aspx";
}
or
2) Insert/update response into database. Redirect user from payment page to VerifyPayment page and get database value here.
Note: Deploy your application and check, sometimes Ajax calls does not work in local.
After investigation i found that we can just include a form from front-side like this example: Pay-fort SDK example,
And no need to render the HTML response from back-side.
When I post to server using HttpWebRequest and method POST, the NameValueCollection in the asp code has no values. I have identical code working with other server pages, the only difference is the string data posted is a bit different.
code that posts is from a c# desktop application:
string responseFromServer = string.Empty;
System.Net.HttpWebRequest request = null;
System.IO.StreamReader reader = null;
System.Net.HttpWebResponse response = null;
string http = string.Empty;
http = "http://www.apageonmywebsite.aspx";
request = HttpWebRequest.Create(http) as HttpWebRequest;
request.Method = "POST";
UTF8Encoding encoding = new UTF8Encoding();
//send a namevalue pair -that is what the website expects via the request object
string postData = "TRIALID=" + System.Web.HttpUtility.UrlEncode(trialUserID, encoding);
byte[] byte1 = encoding.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byte1.Length;
request.Timeout = 20000;
System.IO.Stream newStream = request.GetRequestStream();
newStream.Write(byte1, 0, byte1.Length);
newStream.Close();
System.Threading.Thread.Sleep(1000);
response = (HttpWebResponse)request.GetResponse();
System.IO.Stream dataStream = response.GetResponseStream();
reader = new System.IO.StreamReader(dataStream);
responseFromServer = reader.ReadToEnd();
if (responseFromServer.Contains("\r"))
{
responseFromServer = responseFromServer.Substring(0, responseFromServer.IndexOf("\r"));
}
Server code:
NameValueCollection postedValues = Request.Form; // Request.Form worked locally, failed on server(count=0)
IEnumerator myEnumerator = postedValues.GetEnumerator();
try
{
foreach (string s in postedValues.AllKeys)
{
if (s == "TRIALID")
{
regcode += postedValues[s];
break;
}
}
}
catch (Exception ex)
{
Response.Clear();
Response.Write("FAILED");
this.resultMsg = "FAILED. Exception: " + ex.Message;
LogResult();
return;
}
if (string.IsNullOrEmpty(regcode))
{
Response.Write("postedvalues count=" + postedValues.Count.ToString() + ": no regcode:");
this.resultMsg ="postedvalues count=" + postedValues.Count.ToString() + ": no regcode:";
LogResult();
return;
}
In the sending application, responseFromServer is postedvalues count=0:no regcode:
So the data is posted but not "seen" on the server.
The trialUserID field used in the urlencode method is a string containing user domain name plus user name from the Environment object plus the machine name.
Answer to my own question is that the url needs to be https not http.
I converted my asp.net website to https one year ago and when I created the app that sends the posted data I assumed that since the entire website is configured to automatically redirect to https that should take care of it. Clearly, the webrequest needs the https hardcoded in the url.
Just tried to click the Accept button but that is not allowed for two days since I answered my own question.
I am trying to use the API against our ALM 12.21 server, but always ends up with "401 Unauthorized". It seems that I get the auth cookie back correctly, but when I try to do something after that I am unauthorized.
I use this the get this to get auth cookie (seems to work):
HttpWebRequest myauthrequest = (HttpWebRequest)WebRequest.Create("https://server/qcbin/authentication-point/alm-authenticate");
string AuthenticationXML = #"<alm-authentication>
<user>username</user>
<password>password</password>
</alm-authentication>";
byte[] Requestbytes = Encoding.UTF8.GetBytes(AuthenticationXML);
myauthrequest.Method = "POST";
myauthrequest.ContentType = "application/xml";
myauthrequest.ContentLength = Requestbytes.Length;
myauthrequest.Accept = "application/xml";
Stream RequestStr = myauthrequest.GetRequestStream();
RequestStr.Write(Requestbytes, 0, Requestbytes.Length);
RequestStr.Close();
HttpWebResponse myauthres = (HttpWebResponse)myauthrequest.GetResponse();
var AuthenticationCookie = myauthres.Headers.Get("Set-Cookie");
AuthenticationCookie = AuthenticationCookie.Replace(";Path=/;HTTPOnly", "");
I am not sure if the .Replace is needed. Just read it somewhere. I get 401 both with or without it though, when trying to do subsequent requests.
Trying e.g. this after getting auth cookie:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://server/qcbin/rest/domains/FS/projects/P3602_SLS_Project/defects/1");
req.Method = "GET";
req.ContentType = "application/xml";
req.Accept = "application/octet-stream";
req.Headers.Set(HttpRequestHeader.Cookie, AuthenticationCookie);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream RStream2 = res.GetResponseStream();
XDocument doc = XDocument.Load(RStream2);
Which fails with 401.
Anyone have complete working code for the ALM 12.21 REST API?
You need two main cookies to get the ALM REST API works perfectly.
LWSSO_COOKIE_KEY
QCSession
almURL = "https://..com/qcbin/"
authEndPoint = almURL + "authentication-point/authenticate"
qcSessionEndPoint = almURL + "rest/site-session"
After you get successful response for authEndPoint you will get the LWSSO_COOKIE_KEY
Use that cookie in your next request to qcSessionEndPoint, it should give you QCSession cookie.
Use both LWSSO_COOKIE_KEY and QCSession cookies in your subsequent requests to get data from ALM.
I see that you are using octet-stream to get the defect response. When I checked the documentation, it can return one of the following types.
"application/xml"
"application/atom+xml"
"application/json"
Just in case, if you need to see some working implementation in python, here it is https://github.com/macroking/ALM-Integration/blob/master/ALM_Integration_Util.py
It may give you some idea.
Thank you #Barney. You sent me in the correct direction :-) For anyone interested, I managed it like this, e.g. for getting defect ID 473:
Logging on to create a CookieContainer and then use that to do the actual ALM data fetch:
private void button1_Click(object sender, EventArgs e)
{
string almURL = #"https://url/qcbin/";
string domain = "domain";
string project = "project";
CookieContainer cookieContainer = LoginAlm2(almURL, "username", "password", domain, project);
HttpWebRequest myWebRequest1 = (HttpWebRequest)WebRequest.Create(almURL + "/rest/domains/" + domain + "/projects/" + project + "/defects/473");
myWebRequest1.CookieContainer = cookieContainer;
myWebRequest1.Accept = "application/json";
WebResponse webResponse1 = myWebRequest1.GetResponse();
StreamReader reader = new StreamReader(webResponse1.GetResponseStream());
string res = reader.ReadToEnd();
}
public CookieContainer LoginAlm2(string server, string user, string password, string domain, string project)
{
//Creating the WebRequest with the URL and encoded authentication
string StrServerLogin = server + "/api/authentication/sign-in";
HttpWebRequest myWebRequest = (HttpWebRequest)WebRequest.Create(StrServerLogin);
myWebRequest.Headers[HttpRequestHeader.Authorization] = "Basic " + Base64Encode(user + ":" + password);
WebResponse webResponse = myWebRequest.GetResponse();
CookieContainer c = new CookieContainer();
Uri uri = new Uri(server);
string StrCookie = webResponse.Headers.ToString();
string StrCookie1 = StrCookie.Substring(StrCookie.IndexOf("LWSSO_COOKIE_KEY=") + 17);
StrCookie1 = StrCookie1.Substring(0, StrCookie1.IndexOf(";"));
c.Add(new Cookie("LWSSO_COOKIE_KEY", StrCookie1) { Domain = uri.Host });
//Then the QCSession cookie
string StrCookie2 = StrCookie.Substring(StrCookie.IndexOf("QCSession=") + 10);
StrCookie2 = StrCookie2.Substring(0, StrCookie2.IndexOf(";"));
c.Add(new Cookie("QCSession", StrCookie2) { Domain = uri.Host });
//Then the ALM_USER cookie
string StrCookie3 = StrCookie.Substring(StrCookie.IndexOf("ALM_USER=") + 9);
StrCookie3 = StrCookie3.Substring(0, StrCookie3.IndexOf(";"));
c.Add(new Cookie("ALM_USER", StrCookie3) { Domain = uri.Host });
//And finally the XSRF-TOKEN cookie
string StrCookie4 = StrCookie.Substring(StrCookie.IndexOf("XSRF-TOKEN=") + 12);
StrCookie4 = StrCookie4.Substring(0, StrCookie4.IndexOf(";"));
c.Add(new Cookie("XSRF-TOKEN", StrCookie4) { Domain = uri.Host });
return c;
}
Works like a charm :-)
I'm a C# developer I need to use webhooks to get some stuff after the gethostpage with redirect.
Everything it's fine if I use GET ( get events, get my webhooks ), but when I'm going to create a new webhook I get a "The remote server returned an error: (400) Bad Request." for sure it's a stupid thing but I'm stuck.
Any tips?
The request
byte[] encoded = System.Text.Encoding.Default.GetBytes(apiLogin + ":" + transactionKey);
string base64 = System.Convert.ToBase64String(encoded);
var isPost = !string.IsNullOrWhiteSpace(json);
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/json; charset=utf-8";
httpWebRequest.Method = isPost ? "POST" : "GET";
httpWebRequest.Headers.Add("Authorization", "Basic " + base64);
httpWebRequest.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
if (isPost)
{
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(json);
streamWriter.Flush();
}
}
string result = null;
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
return result;
}
return result;
I'm trying the JSON sample from documentation sample
Found, it is need to create a signature in merchant panel before use "post" webhooks, "get" works also without doing it