Mohammed Omar's Blog

Just another WordPress.com site

Validating TFS 2017 Workitem using TeamFoundationRequestFilter

One of the ways to enforce workitem validation is creating a Team Foundation Request Filter, In my opinion you should always try your best to enforce your rules using workitem template customization, however in case there is some business logic that you need to validate whenever a workitem is edited or added, then one of your options would be to create a Team Foundation Request Filter.

Team Foundation Request Filter lets you intercept TFS Requests to add your own logic to every request. you do that by Implementing:

ITeamFoundationRequestFilter 

This will allow you to intercept the request on the following points


public void BeginRequest(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

public void EndRequest(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

public void EnterMethod(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

public void LeaveMethod(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

public Task PostLogRequestAsync(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

public Task RequestReady(IVssRequestContext requestContext)
 {
 throw new NotImplementedException();
 }

 

 

The method we are interested in here is the BeginRequest, using this method we can stop the request by throwing a RequestFilterException.

TFS Clients (Web portal,VS,VSMAc, etc ) use TFS Restful APIs to communicate with the server, when you Add/Edit a workitem from the portal or from any where, you will be actually making a request with the following URL:

“/tfs/DefaultCollection/_api/_wit/updateWorkItems”

and a bunch of json objects, What we need to do here is to first check if the request is calling the mentioned url , and then parse the json and put our logic, here we are simply checking if Priority is bigger than 2 and through an Request Filter Exception with our message

public void BeginRequest(IVssRequestContext requestContext)
 {
 if (!requestContext.ServiceHost.HostType.HasFlag(TeamFoundationHostType.ProjectCollection)) return;
 if (HttpContext.Current.Request.Url.ToString().Contains("/tfs/DefaultCollection/_api/_wit/updateWorkItems"))
 {
 string content = ReadHttpContextInputStream(HttpContext.Current.Request.InputStream);
 content = HttpUtility.UrlDecode(content);
 var serializer = new JavaScriptSerializer();
 serializer.RegisterConverters(new[] { new DynamicJsonConverter() });

dynamic Workitem = serializer.Deserialize(content.Split('=')[1].Split(new string[] { "&__RequestVerificationToken" }, StringSplitOptions.None)[0], typeof(object));
 if (Workitem[0].fields["10157"]>2)
 {
 throw new ValidationRequestFilterException("Priorities grater than 2 are not allowed.");
 }
 }
 }

Notice here we are not throwing a regular exception, however we are throwing our own custom exception which implement RequestFilterException as following:

class ValidationRequestFilterException : RequestFilterException
 {
 public ValidationRequestFilterException(string x, bool isWeb)
 : base(x, System.Net.HttpStatusCode.OK)
 {
 }

public ValidationRequestFilterException(string x)
 : base(x)
 {
 }
 }

also notice I’m using the following DynamicJsonConverter to convert json to a dynamic object. “all credits for DynamicJsonConverter class goes to Drew Noakes answer here


public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");

return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}

public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}

public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}

#region Nested type: DynamicJsonObject

private sealed class DynamicJsonObject : DynamicObject
{
private readonly IDictionary<string, object> _dictionary;

public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}

public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}

private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);

}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}

result = WrapResultObject(result);
return true;
}

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}

result = WrapResultObject(result);
return true;
}

return base.TryGetIndex(binder, indexes, out result);
}

private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);

var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}

return result;
}
}

#endregion
}

 

Get the complete solution from here

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: