Code.Build.Learn.

Thanks for visiting my blog and hopefully you are entertained & learn something!

Topics

Integrating Google Calendar OAuth API Connection into a Ruby on Rails Application

I've been wanting to do this blog post for awhile now and I'm finally getting to it. I owe the Open Source community for the help they gave me and my partner on this project. I recently finished a pair programming project with a partner in which we built out a corporate calendar for their company.

The general idea behind the build was supposed to be a bolt on calendar application in which visitors to their site could see the upcoming events and elect to RSVP, Favorite or even request to hold an event on the premises. In order to RSVP, Favorite or request an event, a user would have to log-in and create a basic account. Finally, the big stipulation for the application would be that an event would have to be "requested" and subsequently approved manually by an admin before it would be posted to the company's google calendar. That part specifically meant an API call to google in which a hash of data would be sent to Google.

We decided to simply build a straight Ruby on Rails app with Devise for login/logout, Dotenv for protecting secret keys. CRUD for Events & Favorites was achieved with basic "has many" and "through" calls. That was a fun part of the application as well.

The stipulations for the build were:
- [x ] Google Calender
- [x] Login/logout (Devise)
- [x] CRUD events (Rails)
- [x] RSVP and Favorites CRUD (Rails)
- [x] Users can mark event as favorite (Rails)
- [x] Users can mark event as RSVP (Rails)
- [x] Users dashboard page (Rails)
- [x] Design layouts (Bootsrap)
- [x] My upcoming events (Rails)
- [x] An event must be approved by admin to be published(Petergate)

The API Connection was the most difficult part of our application. In the simplest, "10,000ft" view of the API connection, it works as follows:

1) Register your application with Google at the Google Developers Area(described in links below). This shows google your application is trusted AND it also provides information to a user when you application wants to access their calendar, gmail, drive, etc..

2) When a user wants to "sign in" with their Google credentials OR they are allowing your program to make changes to their calendar (think Meetup.com adding events to your personal Google Calendar when you RSVP an event), they are prompted the first time to "allow software program/website to access your calendar/drive/mail". The initial time this happens, your program's Google API secret key and client ID are shared with Google to ID your program.

3) When the user hits "accept", Google returns a "refresh token" that your program needs to store. This refresh token is sent to Google everytime in the future as proof that your program is authorized to access their Google service.

4) From now on, all your program needs to do is utilized Google's built in functions/methods for interacting with their services. In our case it was simply "insert event". The data has to be formatted correctly and is sent as a hash(which is all JSON data is) using get and post HTTP restful commands.

We started out by attempting a manual Signet authorization style connection that is described in these two articles:
1) https://readysteadycode.com/howto-integrate-google-calendar-with-rails
2) https://readysteadycode.com/howto-access-the-gmail-api-with-ruby

These articles were very helpful but left out some very important steps. The issue with these two articles is that they do not address the "refresh token" system that Google uses to register an application as authorized. This took us about 16 hours of coding to figure out this wouldn't work for our cause.

What is unique about our application versus most other api OAuth implementations was that we only had ONE user that needed to be authorized, our admin/corporate Google account. Since the admin was the only one that could approve an event, we simply had to store the data submitted from our "Request an event" form, and then when it was "Approved" with a button click, we reformatted the data into the appropriate hash for Google.

So, after spinning our wheels for 2 days with the Signet authorization, we then stumbled onto the following article which uses Devise and the Omniauth gem for the integration. The article was much more concise and applicable to our use, but it was not 100% complete. We still had to fill in some blanks. It did help us understand the steps that would need to happen with the refresh token step and becoming authorized.

The article: http://www.thegreatcodeadventure.com/using-the-google-api-ruby-client-with-google-calendar-api/

Taking what we learned in the above 3 articles, we finally ended up with the following code:

Events controller:

def approve
    @event.approved!

    @calendar = GoogleCalendarWrapper.new(current_user)
    @calendar.insert(@event.format_for_google)

    respond_to do |format|
      format.html { redirect_to admin_path, notice: 'The event was approved.' }
    end
  end

User.rb model file:

devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:google_oauth2]

  has_many :rsvps, dependent: :destroy
  has_many :favorites, dependent: :destroy
  has_many :events, dependent: :destroy

  validates_presence_of :name

  def self.find_for_google_oauth2(auth)
    data = auth.info
    user = User.where(email: data['email']).first

    user.provider = auth.provider
    user.uid = auth.uid
    user.access_token = auth.credentials.token
    user.refresh_token = auth.credentials.refresh_token
    user.save
  end
end

In our Event.rb model file we formatted the hash data:

def format_for_google
    event_hash = {
      summary: self.title,
      location: self.location,
      start: { dateTime: self.start.strftime("%Y-%m-%dT%H:%M:%S-0600") },
      end: { dateTime: (self.start + self.end.chomp(" hours").to_i.hours).strftime("%Y-%m-%dT%H:%M:%S-0600") },
      description: self.body
    }
  end

The eventual step that finally allowed us to add an event to the corporate Google Calendar, was when we found Google's "insert event" test environment: Google Developers area: https://developers.google.com/calendar/v3/reference/events/insert

It was through this link that we finally could test out our hash and get events posted. It was the final troubleshooting step.

Finally, all we really had to do was hook up our "Approve" button from the admin dashboard to the approve action in the events controller. This allowed us to move send events with a simple button click.

I hope this article helps fill in any gaps that the mentioned articles were unable to clear up. WIthout the other author's articles, we would not have been able to finish this project in a timely manner.

Let me know what comments you have. You need to register to leave comments.

Keep coding!
Justin

Very Helpful Links:

Google Developers area: https://developers.google.com/calendar/v3/reference/events/insert
http://www.thegreatcodeadventure.com/using-the-google-api-ruby-client-with-google-calendar-api/
https://readysteadycode.com/howto-integrate-google-calendar-with-rails
https://readysteadycode.com/howto-access-the-gmail-api-with-ruby