top of page

Blackbaud CRM™ Integration with Google Calendar Part 2

In this post, part 2 of 3, we’re going to take a look at using 2 different APIs to create calendar items. The goal will be to create a web service in Visual Studio that can accept an event, and use that to create calendar events in Google, or in Outlook 365.

In the third and final installment, we will take a look at what we’ve done in parts 1&2, and use Blackbaud CRM™ as the trigger for adding events to Google and Outlook 365.

“Why do we need to have a separate web service?” you may wonder, as the Blackbaud Infinity platform allows developers to create rich customizations, and it’s certainly capable of calling external APIs. This may be true, but in reality, the APIs for Google and Outlook rely on some very specific dependencies, which will load a lot of extra components into CRM’s bin\custom directory, and may not play well either with CRM, or other customizations you will be writing. Additionally, separating the API calls into their own service allows some extra flexibility around testing, as well as the ability to be consumed by applications other than CRM.

Finally, as the Google API uses a proprietary security file, it gives administrators a little more leeway in maintaining credentials, and not have to shoehorn them into a CRM customization.

In this exercise what we’ll do is create a web service that can take a simple event with a collection of attendees, and add it to either a Google Calendar or an Outlook 365 calendar. In doing so we’ll revisit some of the Google API code we saw in Part 1, and also look at some code to work with the Outlook API.

Before you start this exercise, it’s assumed you went through part 1 and created a Google Developer’s account and project, and downloaded a .json security file to your local system. As long as you went up to the point where Part 1 discussed creating a Visual Studio project, you’ll be ok to start here. In this post, we’ll be creating a brand new VS project.

First thing is to create a new web service project:

As we saw in Part one, in order to use the Google API, we need to import some references:

Tools>NuGet Package Manager>Manage NuGet Packages for Solution

Google APIs Auth Client Library

Then do the same thing, searching for “Google.Apis.Calendar.v3 Client Library”

Rename IService1.vb and Service1.svc to ICalendarSvc.vb and CalendarSvc.scv, respectively. Then open up our interface, ICalendarSvc.vb

Now replace the default operation contracts with the following:

<OperationContract()>

Sub AddCalendarEvent(Details As EventDetails)

Next, remove the default Data Contracts and create the following, which is a new composite type. This type will be a relatively simple version of a calendar event, and is sent into our web service as the request:

<DataContract()>

Public Class EventDetails

<DataMember()>

Public Property Mode() As enumCalendarMode

<DataMember()>

Public Property EventTitle() As String

<DataMember()>

Public Property EventBody() As String

<DataMember()>

Public Property EmailAddresses() As List(Of String)

<DataMember()>

Public Property dtStart() As Date

<DataMember()>

Public Property dtEnd() As Date

End Class

The last thing we will do in this file is add an enum to tell the service what mode to use. In our example we will build in two modes – one for Google and another for Outlook365:

Public Enum enumCalendarMode As Integer

CALENDAR_MODE_GOOGLE = 0

CALENDAR_MODE_OUTLOOK = 1

End Enum

The entire file should now look like this:

<ServiceContract()>

Public Interface ICalendarSvc

<OperationContract()>

Sub AddCalendarEvent(Details As EventDetails)

End Interface

<DataContract()>

Public Class EventDetails

<DataMember()>

Public Property Mode() As enumCalendarMode

<DataMember()>

Public Property EventTitle() As String

<DataMember()>

Public Property EventBody() As String

<DataMember()>

Public Property EmailAddresses() As List(Of String)

<DataMember()>

Public Property dtStart() As Date

<DataMember()>

Public Property dtEnd() As Date

End Class

Public Enum enumCalendarMode As Integer

CALENDAR_MODE_GOOGLE = 0

CALENDAR_MODE_OUTLOOK = 1

End Enum

Open CalendarSvc.svc, remove what’s there and paste the following:

Public Class CalendarSvc

Implements ICalendarSvc

Public Sub New()

End Sub

Sub AddCalendarEvent(Details As EventDetails) Implements ICalendarSvc.AddCalendarEvent

Dim cal As ICalendarApi

If Details.Mode = enumCalendarMode.CALENDAR_MODE_GOOGLE Then

cal = New GoogleCalendarSvc

cal.AddEvent(Details)

ElseIf Details.Mode = enumCalendarMode.CALENDAR_MODE_OUTLOOK Then

cal = New Outlook365CalendarSvc

cal.AddEvent(Details)

End If

End Sub

End Class

What we’ll do next is fill in the actual code that does the work for adding the Google and Outlook events. Since the implementation is similar for both, it makes some sense to create an interface. Looking at the code above, we can infer that we’ll be making an interface called “ICalendarApi” and two classes which implement the interface: “GoogleCalendarSvc” and “Outlook365CalendarSvc”. So go ahead and create ICalendarApi, which should be very simple and look like this:

Public Interface ICalendarApi

Sub AddEvent(Details As EventDetails)

End Interface

Next, create a new class called “GoogleCalendarSvc.vb” and paste in the following:

Imports System.IO

Imports System.Threading

Imports Google.Apis.Calendar.v3

Imports Google.Apis.Calendar.v3.Data

Imports Google.Apis.Services

Imports Google.Apis.Auth.OAuth2

Imports Google.Apis.Util.Store

Imports System.Net.Http

Public Class GoogleCalendarSvc

Implements ICalendarApi

Dim _scopes As IList(Of String) = New List(Of String)()

Dim _service As Google.Apis.Calendar.v3.CalendarService

Dim _AllEvents As New List(Of Data.Event)

Dim _Details As EventDetails

Public Sub AddEvent(Details As EventDetails) Implements ICalendarApi.AddEvent

_Details = Details

InitializeService()

CreateEvent()

End Sub

Private Sub InitializeService()

_scopes.Add(Google.Apis.Calendar.v3.CalendarService.Scope.Calendar)

Dim jsonFile As String = "C:\Credentials\client_secret_791792756839-c1ev8q9gjgvep2qm1v2cl872joh2pd98.apps.googleusercontent.com.json"

Dim credential As UserCredential

Dim secrets As ClientSecrets

Dim fds As New FileDataStore("C:\Credentials", True)

Dim googleAccount As String = "brightvinesolutions@gmail.com"

Try

Using stream As New FileStream(jsonFile, FileMode.Open, FileAccess.Read)

secrets = GoogleClientSecrets.Load(stream).Secrets

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, _scopes, googleAccount, CancellationToken.None, fds).Result

End Using

Catch ex As Exception

Throw ex

End Try

' Create the calendar service using an initializer instance

Dim initializer As New BaseClientService.Initializer()

initializer.HttpClientInitializer = credential

initializer.ApplicationName = "VB.NET Calendar Sample"

_service = New Google.Apis.Calendar.v3.CalendarService(initializer)

End Sub

Private Sub CreateEvent()

Try

Dim calEvent As New Google.Apis.Calendar.v3.Data.Event

calEvent.Summary = _Details.EventTitle

calEvent.Description = _Details.EventBody

calEvent.Location = ""

Dim attendees As New List(Of EventAttendee)

Dim evAttendee As EventAttendee

For Each curAttendee As String In _Details.EmailAddresses

evAttendee = New EventAttendee

evAttendee.Email = curAttendee

attendees.Add(evAttendee)

Next

calEvent.Attendees = attendees

Dim evStart As New EventDateTime

evStart.DateTime = _Details.dtStart

Dim evEnd As New Data.EventDateTime

evEnd.DateTime = _Details.dtEnd

calEvent.Start = evStart

calEvent.End = evEnd

Dim createdEvent As [Event] = _service.Events.Insert(calEvent, "primary").Execute

Catch ex As HttpRequestException

Throw ex

Catch ex As Exception

Throw ex

End Try

End Sub

End Class

Note that in InitializeService, you’ll need to change the “dim jsonFile…” to reflect the real location of the json file you downloaded from Google. This location, of course, will need to be accessible by IIS, so make sure the AppPool you’re running under has proper rights.

At this point if we were to go back into CalendarSvc.svc and comment out the following lines: