patterncsharpMinor
GitHub (and Bitbucket) Pushing Events
Viewed 0 times
eventsgithubbitbucketandpushing
Problem
This code is for dispatching and handling certain events from GitHub (and also Bitbucket). Things like pushes, issues, etc. can be handled by this system, and strongly-typed objects will be returned.
I left the XML docs out of all these, as somewhere from 2/3 to 3/4 of the files were XML docs. (We all know how much I love my XML docs.)
The first file here is the
This next file is the GitHub dispatcher. On GitHub:
```
public class EventDispatcher : IEventDispatcher
{
public void Dispatch(string eventKey, string json)
{
switch (eventKey)
{
case CommitCommentEvent.WebhookEventName:
OnCommitCommentReceived(new CommitCommentEventArgs(Deserialze(json)));
break;
case CreateEvent.WebhookEventName:
OnCreateReceived(new CreateEventArgs(Deserialze(json)));
break;
case DeleteEvent.WebhookEventName:
OnDeleteReceived(new DeleteEventArgs(Deserialze(json)));
break;
case DeploymentEvent.WebhookEventName:
OnDeploymentReceived(new DeploymentEventArgs(Deserialze(json)));
break;
case DeploymentStatusEvent.WebhookEventName:
OnDeploymentStatusReceived(new DeploymentStatusEventArgs(Deserialze(json)));
break;
case ForkEvent.WebhookEventName:
OnForkReceived(new ForkEventArgs(Deserialze(json)));
break;
case GollumEvent.WebhookEventName:
OnGollumReceived(new GollumEventArgs(Deserialze(json)));
break;
case IssueCommentEvent.WebhookEventN
I left the XML docs out of all these, as somewhere from 2/3 to 3/4 of the files were XML docs. (We all know how much I love my XML docs.)
The first file here is the
IEventDispatcher which is what handles the event dispatching. This is a common interface between the GitHub and Bitbucket dispatchers. On GitHub: IEventDispatcher.public interface IEventDispatcher
{
T Deserialze(string json);
void Dispatch(string eventKey, string json);
}This next file is the GitHub dispatcher. On GitHub:
GitHub.EventDispatcher.```
public class EventDispatcher : IEventDispatcher
{
public void Dispatch(string eventKey, string json)
{
switch (eventKey)
{
case CommitCommentEvent.WebhookEventName:
OnCommitCommentReceived(new CommitCommentEventArgs(Deserialze(json)));
break;
case CreateEvent.WebhookEventName:
OnCreateReceived(new CreateEventArgs(Deserialze(json)));
break;
case DeleteEvent.WebhookEventName:
OnDeleteReceived(new DeleteEventArgs(Deserialze(json)));
break;
case DeploymentEvent.WebhookEventName:
OnDeploymentReceived(new DeploymentEventArgs(Deserialze(json)));
break;
case DeploymentStatusEvent.WebhookEventName:
OnDeploymentStatusReceived(new DeploymentStatusEventArgs(Deserialze(json)));
break;
case ForkEvent.WebhookEventName:
OnForkReceived(new ForkEventArgs(Deserialze(json)));
break;
case GollumEvent.WebhookEventName:
OnGollumReceived(new GollumEventArgs(Deserialze(json)));
break;
case IssueCommentEvent.WebhookEventN
Solution
Let us tackle this code top down.
This is nice and clean, but I have the feeling that the
A dispatcher should just have only the
Now assume someday jason isn't anymore that hip or will be replaced by some better format, the best would be to add an interface
by the way, setting the
Assume the class would have a
and if we change the signatures of the
the
If this dispatcher is used by any kind of plugin you should ensure that an eventhandler which throws an exception does not break the whole applictation. This can be done by wraping the
A solution to this would be to use the invocationlist of the event and iterate over the eventhandlers wrapped in a
public interface IEventDispatcher
{
T Deserialze(string json);
void Dispatch(string eventKey, string json);
}This is nice and clean, but I have the feeling that the
Deserialize() method is somehow misplaced in that interface.A dispatcher should just have only the
Dispatch() method because that is his job. I don't say that an implementation of this interface shouldn't have a Deserialize() method but that method doesn't need to be in the interface hence it should't be public. public interface IEventDispatcher
{
void Dispatch(string eventKey, string json);
}Now assume someday jason isn't anymore that hip or will be replaced by some better format, the best would be to add an interface
ISerializer of which an implementation will be constructor injected into the EventDispatcher, something along this lines public interface ISerializer
{
T Deserialze(string json);
}public class JasonSerializer : ISerializer
{
public T Deserialze(string json)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return (T)serializer.ReadObject(ms);
}
}
}by the way, setting the
Position property to 0 isn't needed here, because after instantiation of the MemoryStream the Position is 0.public void Dispatch(string eventKey, string json) Assume the class would have a
Dictionary> for each passed in eventKey like so private readonly ISerializer serializer;
public EventDispatcher(ISerializer serializer)
{
this.serializer = serializer;
InitializeEventsDictionary();
}
private T Deserialze(string json)
{
return serializer.Deserialze(json);
}
Private Dictionary> events = Dictionary>();
private void InitializeEventsDictionary()
{
events.Add(CommitCommentEvent.WebhookEventName, (s) => { OnCommitCommentReceived(() => { return new CommitCommentEventArgs(Deserialze(s)); }); });
events.Add(CreateEvent.WebhookEventName, (s) => { OnCreateReceived(() => { return new CreateEventArgs(Deserialze(s)); }); });
....
....
}and if we change the signatures of the
OnXXX methods like so protected void OnCommitCommentReceived(Func e)
{
var del = CommitCommentEventReceived;
del?.Invoke(this, e.Invoke());
}the
Dispatch() method could look like this public void Dispatch(string eventKey, string json)
{
Action action;
if (events.TryGetValue(eventKey, out action))
{
action.Invoke(jason);
}
}If this dispatcher is used by any kind of plugin you should ensure that an eventhandler which throws an exception does not break the whole applictation. This can be done by wraping the
del?.Invoke(this, e.Invoke()); in a try..catch block, but if one of the plugins throws, the whole thing would go in the catch block and therefor any following eventhandler wouldn't get the event. A solution to this would be to use the invocationlist of the event and iterate over the eventhandlers wrapped in a
try..catch like so protected void OnCommitCommentReceived(Func f)
{
var del = CommitCommentEventReceived;
if (del == null) {return;}
CommitCommentEventArgs e = f.Invoke();
foreach(EventHandler handler in del.GetInvocationList())
{
try
{
handler(this, e);
}
catch(Exception ex)
{
// because we don't/can't know which exception is thrown lets catch them all
// and do some logging here if we don't want to swallow them
}
}
}Code Snippets
public interface IEventDispatcher
{
T Deserialze<T>(string json);
void Dispatch(string eventKey, string json);
}public interface IEventDispatcher
{
void Dispatch(string eventKey, string json);
}public interface ISerializer
{
T Deserialze<T>(string json);
}public class JasonSerializer : ISerializer
{
public T Deserialze<T>(string json)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return (T)serializer.ReadObject(ms);
}
}
}private readonly ISerializer serializer;
public EventDispatcher(ISerializer serializer)
{
this.serializer = serializer;
InitializeEventsDictionary();
}
private T Deserialze<T>(string json)
{
return serializer.Deserialze<T>(json);
}
Private Dictionary<string, Action<string>> events = Dictionary<string, Action<string>>();
private void InitializeEventsDictionary()
{
events.Add(CommitCommentEvent.WebhookEventName, (s) => { OnCommitCommentReceived(() => { return new CommitCommentEventArgs(Deserialze<CommitCommentEvent>(s)); }); });
events.Add(CreateEvent.WebhookEventName, (s) => { OnCreateReceived(() => { return new CreateEventArgs(Deserialze<CreateEvent>(s)); }); });
....
....
}Context
StackExchange Code Review Q#106176, answer score: 4
Revisions (0)
No revisions yet.