Skip to content

Instantly share code, notes, and snippets.

@lightman76
Last active August 8, 2017 10:40
Show Gist options
  • Select an option

  • Save lightman76/2357338dcca65fd390e2 to your computer and use it in GitHub Desktop.

Select an option

Save lightman76/2357338dcca65fd390e2 to your computer and use it in GitHub Desktop.
Class to interface with Google Contacts API in Ruby. Adapted from https://gist.github.com/cantino/d1a63045fbfe5fc55a94 . Initialize the class with client which is a hurley client and auth is a Signet::OAuth2::Client - I'm using the versions from the google-api-ruby-client 0.9.x series. Hurley client can be one created straight up or prebuilt fro…
#Adapted from https://gist.github.com/cantino/d1a63045fbfe5fc55a94
module ContactList
class GoogleContactsApi
MAX_RESULTS = "250"
attr_reader :client
def initialize(client, auth)
@client = client
@auth = auth
end
def all_contacts
process_contacts_list(fetch_contacts['feed']['entry'])
end
def query_contacts(search_text)
process_contacts_list(fetch_contacts({q: search_text})['feed']['entry'])
end
def all_groups
process_group_list(fetch_groups['feed']['entry'])
end
def group_members(group)
group_data = fetch_contacts({group: group.id})
process_contacts_list(group_data['feed']['entry'])
end
protected
def execute(uri, options = {})
client.get(uri, options[:parameters]) do |req|
req.header['GData-Version'] = '3.0'
req.header['Content-Type'] = 'application/json'
@auth.apply!(req.header)
end
end
def fetch_contacts(options = {})
params = { 'alt' => 'json',
'max-results' => MAX_RESULTS }
params[:q] = options[:q] if options[:q]
params[:group] = options[:group] if options[:group]
response = execute("https://www.google.com/m8/feeds/contacts/default/full",
parameters: params)
JSON.parse(response.body)
end
def fetch_groups(options = {})
params = { 'alt' => 'json',
'max-results' => '200' }
params[:q] = options[:q] if options[:q]
response = execute("https://www.google.com/m8/feeds/groups/default/full",
parameters: params)
JSON.parse(response.body)
end
def process_group_list(group_list)
(group_list || []).map do |group|
group_cleansed = cleanse_gdata(group) #TODO: do I want to filter out anything?
GoogleGroup.new(group_cleansed)
end
end
def process_contacts_list(contact_list)
(contact_list || []).map do |contact|
contact_raw_data = {
emails: extract_schema(contact['gd$email']),
phone_numbers: extract_schema(contact['gd$phoneNumber']),
handles: extract_schema(contact['gd$im']),
addresses: extract_schema(contact['gd$structuredPostalAddress']),
name_data: cleanse_gdata(contact['gd$name']),
nickname: contact['gContact$nickname'] && contact['gContact$nickname']['$t'],
websites: extract_schema(contact['gContact$website']),
organizations: extract_schema(contact['gd$organization']),
events: extract_schema(contact['gContact$event']),
birthday: contact['gContact$birthday'].try(:[], "when")
}.tap do |basic_data|
# Extract a few useful bits from the basic data
basic_data[:full_name] = basic_data[:name_data].try(:[], :full_name)
primary_email_data = basic_data[:emails].find { |type, email| email[:primary] }
if primary_email_data
basic_data[:primary_email] = primary_email_data.last[:address]
end
end
GoogleContact.new(contact_raw_data)
end
end
# Turn an array of hashes into a hash with keys based on the original hash's 'rel' values, flatten, and cleanse.
def extract_schema(records)
(records || []).inject({}) do |memo, record|
key = (record['rel'] || 'unknown').split('#').last.to_sym
value = cleanse_gdata(record.except('rel'))
value[:primary] = true if value[:primary] == 'true' # cast to a boolean for primary entries
value[:protocol] = value[:protocol].split('#').last if value[:protocol].present? # clean namespace from handle protocols
value = value[:$t] if value[:$t].present? # flatten out entries with keys of '$t'
value = value[:href] if value.is_a?(Hash) && value.keys == [:href] # flatten out entries with keys of 'href'
memo[key] = value
memo
end
end
# Transform this
# {"gd$fullName"=>{"$t"=>"Bob Smith"},
# "gd$givenName"=>{"$t"=>"Bob"},
# "gd$familyName"=>{"$t"=>"Smith"}}
# into this
# { :full_name => "Bob Smith",
# :given_name => "Bob",
# :family_name => "Smith" }
def cleanse_gdata(hash)
(hash || {}).inject({}) do |m, (k, v)|
k = k.gsub(/\Agd\$/, '').underscore # remove leading 'gd$' on key names and switch to underscores
v = v['$t'] if v.is_a?(Hash) && v.keys == ['$t'] # flatten out { '$t' => "value" } results
m[k.to_sym] = v
m
end
end
end
class GoogleContact
attr_accessor :first_name, :last_name, :email_address, :raw_data
def initialize(raw_data)
@raw_data = raw_data
@first_name = raw_data && raw_data[:name_data] ? raw_data[:name_data][:given_name] : nil
@last_name = raw_data && raw_data[:name_data] ? raw_data[:name_data][:family_name] : nil
@email_address = raw_data[:primary_email]
end
def full_name
"#{@first_name} #{@last_name}"
end
end
class GoogleGroup
attr_accessor :title, :id, :raw_data
def initialize(raw_data)
@raw_data = raw_data
@title = raw_data[:title]
@id = raw_data[:id]
end
end
end
@sarafisherman22
Copy link
Copy Markdown

@lightman76, is this the right way to use the class above?

@c = ContactList::GoogleContactsApi.new(client, auth).all_contacts

If so, how do I create client?

@leoshaw
Copy link
Copy Markdown

leoshaw commented Mar 16, 2017

Just in case you haven't seen it, you might be able to achieve what you're after with the People API https://github.com/google/google-api-ruby-client/blob/master/generated/google/apis/people_v1/service.rb

e.g.

    People = Google::Apis::PeopleV1
    service = People::PeopleService.new

...configure auth with service.authorization or service.key

  service.list_person_connections("people/me", sort_order: "FIRST_NAME_ASCENDING",
      request_mask_include_field: "person.names,person.biographies") do |result, error|

@msp
Copy link
Copy Markdown

msp commented Mar 21, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment