Thursday, April 08, 2010

Google Calendar Management with MVC & StructureMap




I recently read John Petersen’s blog entitled “Adding Google Charts to your ASP MVC Applications” and I thought it was a pretty cool read. I thought I’d do a similar one taking advantage of the Google Data API SDK.

In order to get started, you’ll need to do some setup by following the awesome instructions Google provides called Getting Started with the .NET Client Library to setup the SDK.

Let’s get to the code!

First things first, create a calendar repository interface like this:

    public interface ICalendarRepository
{
void Add(Activity activity);
void Delete(string activityId);
Activity GetById(string activityId);
Activity[] GetAll();
}

Now we just need to impelment it. I’ll go through some of this, but I think it’s pretty self-explanatory if you just read through it. I may be wrong :)

    public class GoogleCalendarRepository : ICalendarRepository
{
private const string GoogleCalendarFeed = "http://www.google.com/calendar/feeds/{0}/private/full";

private readonly string _calendarId;
private readonly CalendarService _calendarservice;
public GoogleCalendarRepository(string calendarId, string googleUsername, string googlePassword)
{
_calendarId = calendarId;
_calendarservice =
new CalendarService("Google Calendar");
_calendarservice.setUserCredentials(googleUsername, googlePassword);
}

private string GoogleCalendarFeedWithId
{
get { return string.Format(GoogleCalendarFeed, _calendarId); }
}

public void Add(Activity activity)
{
var entry = new EventEntry(activity.Title, activity.Description, activity.Location);

var eventTime = new When(activity.StartDate, activity.EndDate);
entry.Times.Add(eventTime);

var postUri = new Uri(GoogleCalendarFeedWithId);
var eventEntry = _calendarservice.Insert(postUri, entry);

activity.Id = eventEntry.EventId;
}

public void Delete(string activityId)
{
_calendarservice.Delete(getEventBy(activityId));
}

public Activity GetById(string activityId)
{
return getActivityFrom((EventEntry) getEventBy(activityId));
}

public Activity[] GetAll()
{
var query = new EventQuery();
query.Uri =
new Uri(GoogleCalendarFeedWithId);
var feed = _calendarservice.Query(query);

var activities = new List<Activity>();
foreach(var e in feed.Entries)
activities.Add(getActivityFrom((
EventEntry)e));

return activities.ToArray();
}

private static Activity getActivityFrom(EventEntry eventEntry)
{
return new Activity
{
Description = eventEntry.Content.Content,
StartDate = eventEntry.Times[
0].StartTime,
EndDate = eventEntry.Times[
0].EndTime,
Id = eventEntry.EventId,
Location = eventEntry.Locations[
0].ValueString,
Title = eventEntry.Title.Text
};
}

private AtomEntry getEventBy(string eventId)
{
try
{
var entryQuery = new EventQuery();
entryQuery.Uri =
new Uri(string.Format(GoogleCalendarFeedWithId + "/{0}", eventId));
var result = _calendarservice.Query(entryQuery);
if (result.Entries.Any())
return result.Entries[0];
}
catch {}

throw new EventNotFound();
}
}

Line 1. I inherit from my ICalendarRepository
Line 2. Open Bracket
Line 3. Constant string for the Google Uri – notice I put the {0} in there so I can plop my calendar id in its place later on.
Line 4. Line break…just kidding…I’ll just explain each group of code from here on out.

In the constructor, I take in the calendar id with a google username and google password that are authorized to modify/read the calendar. I played with a couple other authentication methods, but nothing worked as well as the good ole setUserCredentials, so I stuck with it.

NOTE: Get your Calendar Id by following these steps:

  1. Click the dropdown arrow next to your calendar name at http://www.google.com/calendar
  2. Select Calendar Settings
  3. At the bottom of the page, you’ll see “Calendar Address” look to the right of the three images shown in that section and you’ll see the Calendar ID.

After the constructor, I have a private read-only property that just formats my Google Uri with my calendar id.

Also worth noting is that I’m only referencing these namespaces: Google.GData.Calendar; Google.GData.Client; Google.GData.Extensions; in this repository. This is the ONLY place the Google namespace is mentioned in my project.

The Add method is simple enough. It accepts an activity, creates an EventEntry, and uses the calendar service that’s initialized in our constructor to insert the event and then it sets the activity.Id to the new id created.

The Delete method is even easier. It calls the Delete method on the calendar service and the getEventBy() private method to get the event via an EventQuery. I extracted that code out because I was using the code in my Delete and in the next method, GetById.

In GetById, I call another private method called getActivityFrom(), which does the same thing that AutoMapper would have done, but this was easier for this sample. I also needed to extract this code because I was doing it in the GetById & GetAll methods. I obviously could’ve cleaned this method up and added more validation like checking if the collections are null before just referencing them, but this is an example :)

That’s it for the repository!

The EventNotFound exception thrown in the getEventBy method looks like this if you’re wondering:

    public class EventNotFound : Exception
{
public override string Message
{
get { return "Event not found. Activity.Id is required."; }
}
}

The activity class looks like this:

    public class Activity
{
public string Id { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public string Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}

Let’s create the controller real quick as an example of how to use this repository. I’m not going to show the view code because this thing is already pretty long :)  If you think it’s necessary, feel free to comment!

    public class ActivityManagementController : Controller
{
private readonly ICalendarRepository _calendarRepository;

public ActivityManagementController(ICalendarRepository calendarRepository)
{
_calendarRepository = calendarRepository;
}

public ViewResult Add()
{
return View(new Activity());
}

[
AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
public ViewResult Add(ActivityView activity)
{
_calendarRepository.Add(
Mapper.Map(activity));

return View(activity);
}

public ViewResult View(string activityId)
{
var activity = _calendarRepository.GetById(activityId);

return View(activity);
}

public ViewResult ViewAll()
{
var activities = _calendarRepository.GetAll();

return View(activities);
}

public ViewResult Delete(string activityId)
{
_calendarRepository.Delete(activityId);

return View();
}
}

The most important thing to note here is the controller constructor. What it allows us to do is inject whatever repository we want into the controller via StructureMap. So, let’s take a look at the StructureMap configuration.

    public class DependencyRegistry : Registry
{
protected override void configure()
{
Scan(x =>
{
x.TheCallingAssembly();
x.WithDefaultConventions();
});

ForRequestedType<
ICalendarRepository>()
.TheDefault.Is.OfConcreteType<
GoogleCalendarRepository>()
.WithCtorArg(
"calendarId").EqualToAppSetting("calendarId")
.WithCtorArg(
"googleUsername").EqualToAppSetting("googleUsername")
.WithCtorArg(
"googlePassword").EqualToAppSetting("googlePassword");
}
}

That’s all you’ll need to get this thing up and running. If you get the “No parameterless constructor…” error, then you need to look at rolling out your own controller factory that leverages StructureMap. You can see an example in my demo MVC app.

Well, I guess that’s it for now. Obviously there are more items we could include in our Activit object that Google has in their’s, but I mainly wanted to show how to get it setup. Anyhow, I hope you enjoyed it and please feel free to comment.

Thanks for reading!


Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...