Skip to content

Instantly share code, notes, and snippets.

@lightman76
Last active August 8, 2017 10:40
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lightman76/2357338dcca65fd390e2 to your computer and use it in GitHub Desktop.
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

@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

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

msp commented Mar 21, 2017

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