How to access a specific Post parameter in an HTTP Post WebHook? - c#

I'm using JotForms to send a POST Message using the WebHook integration. This is the message that is sent.
RequestBin Message
I'm implementing a WebService in C# using Azure Functions in order to Insert the values from the form to an SQL-Server.
#r "System.Data"
using System.Net;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string name = "";
dynamic body = await req.Content.ReadAsStringAsync();
log.Info(body);
var e = JsonConvert.DeserializeObject<Person>(body as string);
name = e.firstname + " " + e.lastname;
//Connect to SQL
var cnnString = "Server=tcp:XXXX.database.windows.net,1433;"+"Initial Catalog=XXXX;"
+"Persist Security Info=False;"+"User ID=XXX;"+"Password=XXXX;"+"MultipleActiveResultSets=False;"
+"Encrypt=True;"+"TrustServerCertificate=False;"+"Connection Timeout=30;";
using (SqlConnection conn = new SqlConnection(cnnString))
{
conn.Open();
// Insert Signup
var signupInsert = "INSERT INTO [dbo].[test_data] ([firstname],[lastname],[date])" +
"VALUES ('" + e.q8_FirstName + "','" + e.q9_yTus + "','" + e.q24_Birthday + "')";
// Execute and load data into database.
using (SqlCommand cmd = new SqlCommand(signupInsert, conn))
{
var rows = cmd.ExecuteNonQuery();
}
return name == " "
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Ok" );
}
}
public class Person{
public string firstname {get;set;}
public string lastname {get;set;}
public string date {get;set;}
public string q8_FirstName {get;set;}
public string q9_yTus {get;set;}
public string q24_Birthday {get;set;}
}
I've managed to insert sucessfully when I test with a body that is just the JSON
{
"firstname": "ni\u00f1o",
"lastname": "Lachner2",
"date":"08/08/1994",
"name":"Vincenz2",
"slug":"submit\/80565829893878\/",
"input_language":"Espa\u00f1ol",
"q8_FirstName":"Nombre",
"q9_yTus":"Apellido",
"q24_Birthday":"08\/08\/1994",
"q5_cedula":"115850853",
"q18_country":"Costa Rica",
"dropdown_search":"",
"q3_province":"San Jos\u00e9",
"q11_cantonSanJose":"Alajuelita",
"q12_cantonAlajuela":"",
"q13_cantonHeredia":"",
"q14_cantonCartago":"",
"q15_cantonPuntarenas":"",
"q16_cantonPuntarenas":"",
"q17_cantonGuanacaste":"",
"q6_phone":"88141833",
"q2_mail":"vincenz.lachner#gmail.com",
"q7_shirtSize":"S",
"q25_channel":{"0":"Correo electr\u00f3nico","other":"YOU"},
"q27_politicaDe":"Accepted",
"preview":"true"
}
How can i access the JSON in the rawRequest? That is in the RawBody under the name rawRequest.
and what are these separators? --------------------------e5d83c25c3d6dcc0
--------------------------e5d83c25c3d6dcc0
Content-Disposition: form-data; name="rawRequest"
{"slug":"submit\/80565829893878\/","input_language":"Espa\u00f1ol","q8_FirstName":"textbox_sample0","q9_yTus":"textbox_sample1","q24_Birthday":"11\/11\/1111","q5_cedula":"1","q18_country":"Costa Rica","dropdown_search":"","q3_province":"San Jos\u00e9","q11_cantonSanJose":"Alajuelita","q12_cantonAlajuela":"","q13_cantonHeredia":"","q14_cantonCartago":"","q15_cantonPuntarenas":"","q16_cantonPuntarenas":"","q17_cantonGuanacaste":"","q6_phone":"1","q2_mail":"john#example.com","q7_shirtSize":"XS","q25_channel":["Facebook"],"q27_politicaDe":"Accepted","preview":"true"}

It should be something like
var form = await req.Content.ReadAsFormDataAsync();
var body = form["rawRequest"];

If you send the message from JotForms, You could use the following code to get the
rawRequest or you could get pretty property value that is the fields in the JotForms.
The following is my detail steps:
1.Create the JotForms
2.Add the follow code to get the rawRequest
if (req.Content.IsMimeMultipartContent())
{
var content = await req.Content.ReadAsMultipartAsync();
var test = content.Contents.ToList();
Dictionary<string, string> dic = new Dictionary<string, string>();
foreach (var item in test)
{
var value = await item.ReadAsStringAsync();
dic.Add(item.Headers.ContentDisposition.Name, value);
log.Info(value);
}
foreach (var item in dic)
{
log.Info($"{item.Key}:{item.Value}");
}
}
3. Remote debug on my side.

Related

ASP.NET Core 2.1 Calling Rest API in a schedule

I have this ASP.NET Core 2.1 web application. The admin gets online game codes from the page below. Selecting the game (Razer Gold Pin), quantity and email to send those codes.
A Rest API call is made for the purchase. There is a limit for purchasing, a maximum of 200 purchases can be made at one time.
At first, there wasn't much to buy so there was no problem. But the demand has increased, when there are 10 thousand, 20 thousand purchases, it is necessary to purchase from this screen for hours. I wonder can we make a large number of purchases without waiting in front of the screen with scheduling?
Here is the the Javascript in the cshtml:
$("#btn_add").html(
'<span class="spinner-border spinner-border-sm" role="status" id="spinner" aria-hidden="true"></span> Loading...'
);
var selText = $("#validationCustom04").val();
var gameName = $("#validationCustom04 option:selected").text();
var quantity = $("#quantity").val();
var email = $("#email").val();
var price = $("#total").val();
var company = $("#validationCustom05").val();
if ($("#total").val() !== '') {
price = $("#total").val();
}
var serviceUrl = '/GameBanks/A101PinAsync?quantity=' + quantity + '&game=' + selText + '&email=' + email + '&prc=' + price + '&gameName=' + gameName + '&company=' + company;
$.ajax({
type: "GET",
url: serviceUrl,
dataType: 'json',
success: function (data) {
//alert(JSON.stringify(data));
ShowResult(data);
$("#spinner").remove();
$("#btn_add").html('Add');
}, error: function (xhr, status, error) {
$("#spinner").remove();
$("#btn_add").html('Add');
var errorMessage = xhr.responseText;
alert('Error - ' + errorMessage);
}
});
Here is the controller method:
[HttpGet]
public async Task<IActionResult> A101PinAsync(int quantity, string game, string email, int prc, string gameName, string company)
{
var price = 0;
try
{
string result = null;
var dList = new DemoList();
if (prc > 0)
{
price = prc;
}
var games = new Game { Product = game, Quantity = quantity };
var content = await games.CallGameAsync("Test", "12345", game, quantity, company);
var deserializedResult = JObject.Parse(content);
var root = JsonConvert.DeserializeObject<Root>(content);
if ((string)deserializedResult["coupons"]?[0]?["Serial"] == null)
{
result = result + gameName + ":" + (string)deserializedResult["Message"] + "\n";
}
else
{
foreach (var coupon in root.coupons)
{
result = result + gameName + " Serial:" + coupon.Serial + " Pin:" + coupon.Pin + "\n";
}
}
dList.gameList = result;
// NLOG
NLogPin(logger, User.Identity.Name, DateTime.Now, result, email);
return new JsonResult(dList);
}
catch (Exception e)
{
// NLOG
NLog(logger2, "OyunPalas " + e.Message, DateTime.UtcNow,0);
return StatusCode(500,e.Message);
}
}
Here is the Web API calling method:
public class Game
{
public string Username { get; set; }
public string Password { get; set; }
public string Product { get; set; }
public int Quantity { get; set; }
public string ShopNo { get; set; }
private static readonly Logger logger2 = LogManager.GetLogger("exceptionFile");
public async Task<string> CallGameAsync(string username, string password, string product, int quantity, string company)
{
try
{
HttpWebRequest request = null;
request = (HttpWebRequest)WebRequest.Create("http://111.111.111.111:1907//api/v2/web/purchase");
var svcCredentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(username + ":" + password));
request.Headers.Add("Authorization", "Basic " + svcCredentials);
request.Method = "POST";
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("productCode", product),
new KeyValuePair<string, string>("quantity", quantity.ToString()),
new KeyValuePair<string, string>("shopNo", company),
new KeyValuePair<string, string>("safeNo", company),
new KeyValuePair<string, string>("cashierNo", company)
});
var urlEncodedString = await content.ReadAsStringAsync();
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
await streamWriter.WriteAsync(urlEncodedString);
}
var httpResponse = (HttpWebResponse) (await request.GetResponseAsync());
var response = new HttpResponseMessage
{
StatusCode = httpResponse.StatusCode,
Content = new StreamContent(httpResponse.GetResponseStream()),
};
//Read response
var htmlResponse = await response.Content.ReadAsStringAsync();
return htmlResponse;
}
catch (Exception e)
{
//NLOG
NLog(logger2, "Test" + e.Message);
throw;
}
}
public void NLog(Logger logger, string user)
{
var sb = new StringBuilder();
sb.AppendLine("Test: " + user);
logger.Info(sb.ToString());
}
}
I have to pass product (game type) to the job. I read the https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/more-about-jobs.html#jobdatamap documentation but not fully understand how to use in my case. And I wonder if I can stop/cancel the job from this page before its end time?
I really appreciate any help you can provide. I'm sorry if the question is too silly as I have no experience with this scheduling.
Edit:
I thought of a solution like this, but I'm not sure if it's viable.
The user will enter which game, how many they want to buy, and their e-mail address on the screen.
This data will be saved in a table (BulkRequest) containing this information and the status field in the SQL database. (game: ABC, quantity:20000, status:0)
One IHostedService will get the saved data in a schedule which status=0 from the BulkRequest table and the save requests with status information in a new table (GameRequests) in multiples of 100. (game: ABC, quantity:100, status:0)
Another IHostedService will get the records in 10 minutes schedule which status=0 from the GameRequests table and make the 3rd party API call. Write a response to another table (GameResponses) and set the status=1 if there is no error.
When all requests are completed (status = 1), the status in the BulkRequest table will also be updated to 1.
A third IHostedService will check the status of BulkRequest, if status=1 then maybe a fourth service will prepare a file and send the responses to the e-mail.
I do suggest reviewing message queuing tools such RabbitMQ. With this architecture you will do all the jobs using messaging protocols with no need for "scheduling" and any "x minutes schedule" s would be ommited. Also it is more stable and error handling could be done using best practice standards. More important, it is scalable and you can have multiple hostedServices even on different servers process items in queue:
When request is registered send a message (publish) to the queue.
The hostedService1 receives the item and process it (consume). Multiple services may subscribe to process requests.
The hostedService1 may do all the job it self or even dispatch some parts to other services using the same method
The solution you proposed is doing the same job with an ad-hoc process. In fact you are reinventing the wheel. Hope it helps.

Azure Functions Script Compilation Failed

I'm trying to create an Azure Function with Docker. When I create a Function with func new, it works fine and when I go to http://localhost:8182/api/HttpTriggerCSharp?name=John I get the message
Hello, John
Now I'm trying to run the same project but I changed the code. The previous code was this:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static IActionResult Run(HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = new StreamReader(req.Body).ReadToEnd();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
Now this is my new code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static IActionResult Run(HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// Parsing query parameters
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
log.Info("name = " + name);
string numberOfTerms = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "numberOfTerms", true) == 0)
.Value;
log.Info("name = " + numberOfTerms);
// Validating the parameters received
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(numberOfTerms))
{
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"Please pass a name and the number of digits on the query string.");
return errorResponse;
}
int termsToShow;
try
{
termsToShow = Int32.Parse(numberOfTerms);
}
catch (FormatException e)
{
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"The numberOfTerms parameter must be an integer!");
return errorResponse;
}
if (termsToShow < 0 || termsToShow > 100) {
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest,
"Please pass a numberOfTerms parameter between 0 and 100.");
return errorResponse;
}
// Building the response
string incompleteResponse = "Hello, " + name + ", you requested the first " + numberOfTerms + " terms of the Fibonacci series. Here they are: ";
string completeResponse = GenerateFibonacciTerms(incompleteResponse, termsToShow);
var response = req.CreateResponse(HttpStatusCode.OK, completeResponse);
// Returning the HTTP response with the string we created
log.Info("response = " + response);
return response;
}
public static string GenerateFibonacciTerms(string incompleteResponse, int termsToShow)
{
int a = 0;
int b = 1;
string temporalString = "";
for (int i = 0; i < termsToShow; i++)
{
int temp = a;
a = b;
b = temp + b;
temporalString = temporalString + temp.ToString() + " ";
}
string result = incompleteResponse + temporalString + "- That's it, have an awesome day!";
return result;
}
I build the container then I run it and I get this message:
I've already checked my code with VS Code (I did it in Sublime Text so I didn't have code checking) and all the problems it finds are the same error:
And my code has "errors" everywhere:
How can I solve this?
You are using v2 function core tools(based on .net core), while the code you changed is targeted at v1(.net framework).
So you have two choices:
Uninstall v2 and use function core tool v1.
Modify your code to make it work in v2 runtime.
Here the code for you to refer. GenerateFibonacciTerms method needs no change.
log.Info("C# HTTP trigger function processed a request.");
// Parsing query parameters
string name = req.Query["name"];
log.Info("name = " + name);
string numberOfTerms = req.Query["numberOfTerms"];
log.Info("numberOfTerms = " + numberOfTerms);
// Validating the parameters received
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(numberOfTerms))
{
return new BadRequestObjectResult("Please pass a name and the number of digits on the query string.");
}
int termsToShow;
try
{
termsToShow = Int32.Parse(numberOfTerms);
}
catch (FormatException e)
{
return new BadRequestObjectResult("The numberOfTerms parameter must be an integer!");
}
if (termsToShow < 0 || termsToShow > 100) {
return new BadRequestObjectResult("Please pass a numberOfTerms parameter between 0 and 100.");
}
// Building the response
string incompleteResponse = "Hello, " + name + ", you requested the first " + numberOfTerms + " terms of the Fibonacci series. Here they are: ";
string completeResponse = GenerateFibonacciTerms(incompleteResponse, termsToShow);
var response = new OkObjectResult(completeResponse);
// Returning the HTTP response with the string we created
log.Info("response = " + response);
return response;

C# Web API Error 404 on POST

I am getting an error 404 when I try and so a post on my web api and i'm not sure why i'm getting it the url and everything are correct.
http://10.0.1.96/testwebapi/api/case/UpdateCasePersonal/?id=4584&forename=Andy&surname=Wilson&email=example#example.co.uk&telephone=0166%20254%204876&mobile=0733333333&title=Mr
That is my Url to the web api code that I will put next
[HttpPost]
[Route("updatecasepersonal/")]
public string UpdateCasePersonal(string Caseid, string Title, string Forename, string Surname, string Telephone, string Email, string Mobile)
{
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
var query = $#"UPDATE TestDB.dbo.[crm-data] SET Title=" + Title + ", Forename=" + Forename + ", Surname=" + Surname + ", Telephone=" + Telephone + ", Email=" + Email + ", Mobile=" + Mobile + " WHERE Caseid=" + Caseid;
using (SqlCommand cmd = new SqlCommand(query, con))
{
cmd.CommandType = CommandType.Text;
var dtb = new DataTable();
var da = new SqlDataAdapter(cmd);
da.Fill(dtb);
return "Done";
}
}
}
also I am doing it right trying to update my table like that? or have I done everything wrong as i'm not fluent in c# yet
can provide more code if needed
This is my code that calls the api
onUpdateClick(e) {
this.setState({
updatedForename: this.state.Case.Forename,
updatedSurname: this.state.Case.Surname,
updatedHomeTelephone: this.state.Case.Telephone,
updatedMobileTelephone: this.state.Case.Mobile,
updatedEmail: this.state.Case.Email,
updatedTitle: this.state.titleValue,
updatedPurpose: this.state.purposeValue,
updatedMaritalStatus: this.state.maritalValue,
updatedEmpStatus: this.state.empValue,
}, function () {
var id = this.state.Case.Caseid;
var forename = this.state.updatedForename;
var surname = this.state.updatedSurname;
var email = this.state.updatedEmail;
var homeTelephone = this.state.updatedHomeTelephone;
var mobileTelephone = this.state.updatedMobileTelephone;
var title = this.state.updatedTitle;
axios.post('http://10.0.1.96/testwebapi/api/case/UpdateCasePersonal/', {
params: {
id: id,
forename: forename,
surname: surname,
email: email,
telephone: homeTelephone,
mobile: mobileTelephone,
title: title
}
}).then(function (res) {
}).catch(function (err) {
});
});
this.setState({
hasSaved: true
});
}
If you really want to send the concatenated data in the URL, do something like this:
[HttpPut]
[Route("updatecasepersonal/{Caseid}/{Title}/{Forename}/{Surname}/{Email}/{Telephone}/{Mobile}")]
public string UpdateCasePersonal(string Caseid, string Title, string Forename, string Surname, string Telephone, string Email, string Mobile)
{
...
}
And your url should simply look like :
http://10.0.1.96/testwebapi/api/case/UpdateCasePersonal/4584/Mr/Andy/Wilson/example#example.co.uk/0166%20254%204876/0733333333/
This is not good practice.
This completely exposes your data in the request. In general, concatenation is almost never the best way to do anything related to data. You SHOULD send the data as a whole to the call instead. Something like:
[HttpPut]
[Route("updatecasepersonal/{CaseId}"]
public string UpdateCasePersonal(string Caseid, RequestDto request)
{
...
}
Of course, RequestDto should be a class you make that requires all those fields: Title, Forename, Surname, Email, etc, and pass it in your Javascript (or wherever your sending the post from). And it should be named something apt to your request. Like since this looks like user profile data, or something along those lines, something like ContactDto.

Force re-authentication using OAuthWebSecurity with Facebook

My website is using facebook as it's oauth provider. Users will be able to buy things through my site so I want to force them to authenticate even if they already have an active session with facebook.
I found this link in facebook's api documentation that discusses reauthentication but I can't get it to work with my mvc app. Anyone know if this is possible?
var extra = new Dictionary<string, object>();
extra.Add("auth_type", "reauthenticate");
OAuthWebSecurity.RegisterFacebookClient(
appId: "**********",
appSecret: "**********************",
displayName: "",
extraData: extra);
Found the solution. I had to create my own client instead of using the default one provided by OAuthWebSecurity.RegisterFacebookClient
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
namespace Namespace.Helpers
{
public class MyFacebookClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{
private const string AuthorizationEP = "https://www.facebook.com/dialog/oauth";
private const string TokenEP = "https://graph.facebook.com/oauth/access_token";
private readonly string _appId;
private readonly string _appSecret;
public MyFacebookClient(string appId, string appSecret)
: base("facebook")
{
this._appId = appId;
this._appSecret = appSecret;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
return new Uri(
AuthorizationEP
+ "?client_id=" + this._appId
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&scope=email,user_about_me"
+ "&display=page"
+ "&auth_type=reauthenticate"
);
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
WebClient client = new WebClient();
string content = client.DownloadString(
"https://graph.facebook.com/me?access_token=" + accessToken
);
dynamic data = Json.Decode(content);
return new Dictionary<string, string> {
{
"id",
data.id
},
{
"name",
data.name
},
{
"photo",
"https://graph.facebook.com/" + data.id + "/picture"
},
{
"email",
data.email
}
};
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(content);
if (nameValueCollection != null)
{
string result = nameValueCollection["access_token"];
return result;
}
return null;
}
}
}
and then in AuthConfig.cs...
OAuthWebSecurity.RegisterClient(
new MyFacebookClient(
appId: "xxxxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxx"),
"facebook", null
);
As a note to those that happen upon this if your Facebook Authentication stopped working when v2.3 became the lowest version you have access to (non versioned calls get the lowest version an app has access to). The API now returns JSON and not name value pairs so you have to update the QueryAccessToken method shown above by #Ben Tidman
Below is the updated method
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
dynamic json = System.Web.Helpers.Json.Decode(content);
if (json != null)
{
string result = json.access_token;
return result;
}
return null;
}
There's one issue in using the MyFacebookClient implementation.
Probably someone tryin to implement it came across the error:
The given key was not present in the dictionary
attempting to call the ExternalLoginCallback method in ActionController.
The error raises when the method
OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
is called.
In order to get it working the method VerifyAuthentication has to be overridden.
In particular the
public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl);
overload of the abstract class OAuth2Client.
If you use the following:
public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(QueryAccessToken(returnPageUrl, code));
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
AuthenticationResult result = new AuthenticationResult(true, ProviderName, userData["id"], userData["name"], userData);
userData.Remove("id");
userData.Remove("name");
return result;
}
}
finally you get the method called in the right way and no excpetion is thrown.

Using C#, how do I set a Rally weblink field?

Here's a screenshot of the field I'm trying to update:
How do I update the URL field?
WebLink type fields consist of two parts: LinkID and DisplayString. In order to set a LinkID (which corresponds to the variable ${id} in your screenshot, a DisplayString needs also to be set to an empty string.
Here is a full code example that uses Rally .NET REST toolkit:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Rally.RestApi;
using Rally.RestApi.Response;
namespace aRestApp_CollectionOfTasks
{
class Program
{
static void Main(string[] args)
{
//Initialize the REST API
RallyRestApi restApi;
restApi = new RallyRestApi("user#co.com", "secret", "https://rally1.rallydev.com", "v2.0");
//Set our Workspace and Project scopings
String workspaceRef = "/workspace/1111"; //please replace this OID with an OID of your workspace
String projectRef = "/project/2222"; //please replace this OID with an OID of your project
bool projectScopingUp = false;
bool projectScopingDown = true;
Request storyRequest = new Request("Defect");
storyRequest.Workspace = workspaceRef;
storyRequest.Project = projectRef;
storyRequest.ProjectScopeUp = projectScopingUp;
storyRequest.ProjectScopeDown = projectScopingDown;
storyRequest.Fetch = new List<string>()
{
"Name",
"_ref",
"c_JiraLink"
};
storyRequest.Query = new Query("FormattedID", Query.Operator.Equals, "DE170");
QueryResult queryStoryResults = restApi.Query(storyRequest);
foreach (var s in queryStoryResults.Results)
{
Console.WriteLine(" Name: " + s["Name"] + " JiraLink's DisplayString: " + s["c_JiraLink"]["DisplayString"] + " JiraLink's LinkID: " + s["c_JiraLink"]["LinkID"]);
DynamicJsonObject toUpdate = new DynamicJsonObject();
DynamicJsonObject myLink = new DynamicJsonObject();
myLink["LinkID"] = "NM-3";
myLink["DisplayString"] = "";
toUpdate["c_JiraLink"] = myLink;
OperationResult updateResult = restApi.Update(s["_ref"], toUpdate);
}
}
}
}
Note that this is not different from a more general example of setting LinkID of a WebLink type of filed using a browser's REST client.
Method: POST
URL:
https://rally1.rallydev.com/slm/webservice/v2.0/defect/3807704995?key=abc123...
Request Body:
{
"defect":
{
"c_JiraLink":{
"DisplayString":"",
"LinkID":"NM-2"
}
}
}

Categories

Resources