contactually-rb 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +51 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1158 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +70 -0
- data/LICENSE +21 -0
- data/README.md +76 -0
- data/Rakefile +6 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/contactually-api.gemspec +38 -0
- data/lib/contactually.rb +72 -0
- data/lib/contactually/base.rb +88 -0
- data/lib/contactually/buckets.rb +14 -0
- data/lib/contactually/client.rb +36 -0
- data/lib/contactually/collection.rb +33 -0
- data/lib/contactually/contacts.rb +14 -0
- data/lib/contactually/interface.rb +50 -0
- data/lib/contactually/me.rb +18 -0
- data/lib/contactually/middleware/error_middleware.rb +47 -0
- data/lib/contactually/models/address.rb +17 -0
- data/lib/contactually/models/bucket.rb +17 -0
- data/lib/contactually/models/contact.rb +33 -0
- data/lib/contactually/models/email_address.rb +11 -0
- data/lib/contactually/models/model.rb +95 -0
- data/lib/contactually/models/phone_number.rb +11 -0
- data/lib/contactually/models/social_media_profile.rb +11 -0
- data/lib/contactually/models/tag.rb +13 -0
- data/lib/contactually/models/task.rb +33 -0
- data/lib/contactually/models/user.rb +19 -0
- data/lib/contactually/models/website.rb +11 -0
- data/lib/contactually/response.rb +55 -0
- data/lib/contactually/tags.rb +14 -0
- data/lib/contactually/tasks.rb +14 -0
- data/lib/contactually/version.rb +3 -0
- metadata +197 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Client
|
3
|
+
attr_reader :interface
|
4
|
+
|
5
|
+
def initialize(api_key: nil, auth_token: nil, base_url: 'https://api.contactually.com')
|
6
|
+
@api_key = api_key
|
7
|
+
@auth_token = auth_token
|
8
|
+
@base_url = base_url
|
9
|
+
@interface = Contactually::Interface.new(
|
10
|
+
api_key: @api_key,
|
11
|
+
auth_token: @auth_token,
|
12
|
+
base_url: @base_url
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def buckets
|
17
|
+
@buckets ||= Contactually::Buckets.new(interface: interface)
|
18
|
+
end
|
19
|
+
|
20
|
+
def contacts
|
21
|
+
@contacts ||= Contactually::Contacts.new(interface: interface)
|
22
|
+
end
|
23
|
+
|
24
|
+
def me
|
25
|
+
@contacts ||= Contactually::Me.new(interface: interface)
|
26
|
+
end
|
27
|
+
|
28
|
+
def tags
|
29
|
+
@tags ||= Contactually::Tags.new(interface: interface)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tasks
|
33
|
+
@tasks ||= Contactually::Tasks.new(interface: interface)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Collection
|
3
|
+
extend Forwardable
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :items, :next_page, :previous_page, :total
|
7
|
+
|
8
|
+
def initialize(items, meta: {})
|
9
|
+
@items = items
|
10
|
+
|
11
|
+
@next_page = meta['next_page']
|
12
|
+
@previous_page = meta['previous_page']
|
13
|
+
@total = meta['total']
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
items.each do |item|
|
18
|
+
block.call(item)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_next_page?
|
23
|
+
!next_page.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_previous_page?
|
27
|
+
!previous_page.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def_delegator :items, :last
|
31
|
+
def_delegator :items, :size
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Interface
|
3
|
+
attr_reader :adapter, :api_key, :auth_token, :base_url, :connection, :headers
|
4
|
+
|
5
|
+
def initialize(auth_token: nil, api_key: nil, base_url: nil)
|
6
|
+
@auth_token = auth_token
|
7
|
+
@api_key = api_key
|
8
|
+
@adapter = Contactually.configuration.adapter || :net_http
|
9
|
+
@base_url = base_url
|
10
|
+
|
11
|
+
set_headers
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(url, params)
|
15
|
+
connection.get(build_full_url(url), params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def connection
|
19
|
+
@connection ||= Faraday.new do |conn|
|
20
|
+
conn.adapter @adapter
|
21
|
+
conn.response :json, :content_type => /\bjson$/
|
22
|
+
conn.use Contactually::Middleware::ErrorHandler
|
23
|
+
|
24
|
+
conn.headers['Content-type'] = 'application/json',
|
25
|
+
conn.headers['Authorization'] = "Bearer #{token}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_full_url(url)
|
32
|
+
if url.downcase.start_with?('http')
|
33
|
+
url
|
34
|
+
else
|
35
|
+
"#{base_url}#{url}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def token
|
40
|
+
auth_token || api_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_headers
|
44
|
+
@headers = {
|
45
|
+
'Content-type' => 'application/json',
|
46
|
+
'Authorization' => "Bearer #{token}"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Contactually
|
2
|
+
module Middleware
|
3
|
+
class ErrorHandler < Faraday::Middleware
|
4
|
+
def call(env)
|
5
|
+
@app.call(env).on_complete do |env|
|
6
|
+
case env.status
|
7
|
+
when 401
|
8
|
+
# Missing or invalid auth token
|
9
|
+
raise Contactually::UnauthorizedError.new(error_messages(env.response))
|
10
|
+
when 403
|
11
|
+
# App does not have permission to perform the operation
|
12
|
+
raise Contactually::ForbiddenError.new(error_messages(env.response))
|
13
|
+
when 404
|
14
|
+
# Standard REST resource not found or not allowed to fetch
|
15
|
+
raise Contactually::NotFoundError.new(error_messages(env.response))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def error_messages(response)
|
23
|
+
return generic_error unless is_json?(response)
|
24
|
+
|
25
|
+
body = parse_body(response)
|
26
|
+
|
27
|
+
if body.has_key?('errors')
|
28
|
+
body['errors']
|
29
|
+
else
|
30
|
+
generic_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def generic_error
|
35
|
+
['Something went wrong']
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_json?(response)
|
39
|
+
response.headers['content-type'] =~ /application\/json/i
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_body(response)
|
43
|
+
JSON.parse(response.body) rescue {}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Contactually
|
2
|
+
module Models
|
3
|
+
class Address
|
4
|
+
include Model
|
5
|
+
|
6
|
+
field :city, :string
|
7
|
+
field :country, :string
|
8
|
+
field :id, :string
|
9
|
+
field :full_address, :string
|
10
|
+
field :label, :string
|
11
|
+
field :state, :string
|
12
|
+
field :street_1, :string
|
13
|
+
field :street_2, :string
|
14
|
+
field :zip, :string
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Contactually
|
2
|
+
module Models
|
3
|
+
class Bucket
|
4
|
+
include Model
|
5
|
+
|
6
|
+
field :contact_count, :integer
|
7
|
+
field :created_at, :datetime
|
8
|
+
field :goal, :string
|
9
|
+
field :id, :string
|
10
|
+
field :name, :string
|
11
|
+
field :reminder_interval, :integer
|
12
|
+
field :updated_at, :datetime
|
13
|
+
|
14
|
+
field :extra_data, Hash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Contactually
|
2
|
+
module Models
|
3
|
+
class Contact
|
4
|
+
include Model
|
5
|
+
|
6
|
+
field :addresses, [Address]
|
7
|
+
field :avatar_url, :string
|
8
|
+
field :company, :string
|
9
|
+
field :created_at, :datetime
|
10
|
+
field :email_addresses, [EmailAddress]
|
11
|
+
field :first_name, :string
|
12
|
+
field :id, :string
|
13
|
+
field :last_name, :string
|
14
|
+
field :phone_numbers, [PhoneNumber]
|
15
|
+
field :social_media_profiles, [SocialMediaProfile]
|
16
|
+
field :tags, [String]
|
17
|
+
field :title, :string
|
18
|
+
field :websites, [Website]
|
19
|
+
field :updated_at, :datetime
|
20
|
+
|
21
|
+
# field :custom_fields, [String] TODO: should be it's own model type
|
22
|
+
field :extra_data, Hash
|
23
|
+
|
24
|
+
def first_email_address
|
25
|
+
if email_addresses
|
26
|
+
email_addresses.first
|
27
|
+
else
|
28
|
+
EmailAddress.new({})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Contactually
|
2
|
+
module Models
|
3
|
+
module Model
|
4
|
+
def self.included(base)
|
5
|
+
base.send :extend, ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(params={})
|
9
|
+
params.each do |attr, _value|
|
10
|
+
self.send("#{attr}=", params.delete(attr)) if respond_to?(attr)
|
11
|
+
end if params
|
12
|
+
|
13
|
+
@_extra_attributes = params
|
14
|
+
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
self.class.field_list.inject({}) do |acc, field_name|
|
20
|
+
value = send(field_name)
|
21
|
+
|
22
|
+
if Array === value
|
23
|
+
if value.first.respond_to?(:attributes)
|
24
|
+
value = value.map(&:attributes)
|
25
|
+
else
|
26
|
+
value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
acc.merge(field_name.to_s => value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :_extra_attributes
|
37
|
+
|
38
|
+
def type_cast(value, type)
|
39
|
+
case type
|
40
|
+
when :string then value.to_s
|
41
|
+
when :integer then value.to_i
|
42
|
+
when :float then value.to_f
|
43
|
+
when :date then value.to_date
|
44
|
+
when :datetime then DateTime.parse(value)
|
45
|
+
else
|
46
|
+
case type
|
47
|
+
when Array
|
48
|
+
value.map { |obj| type.first.new(obj) }
|
49
|
+
else
|
50
|
+
if type == Hash
|
51
|
+
value
|
52
|
+
else
|
53
|
+
type.new(value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(method, *args, **kwargs)
|
60
|
+
string_method = method.to_s
|
61
|
+
|
62
|
+
if _extra_attributes.has_key?(string_method)
|
63
|
+
_extra_attributes[string_method]
|
64
|
+
else
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
attr_accessor :field_list
|
71
|
+
|
72
|
+
def field(field_name, type)
|
73
|
+
register_field field_name
|
74
|
+
|
75
|
+
private_method = define_method "#{field_name}=" do |value|
|
76
|
+
instance_variable_set "@#{field_name}", type_cast(value, type)
|
77
|
+
end
|
78
|
+
private private_method
|
79
|
+
end
|
80
|
+
|
81
|
+
def field_list
|
82
|
+
@field_list ||= []
|
83
|
+
end
|
84
|
+
|
85
|
+
def register_field(field_name)
|
86
|
+
class_eval do
|
87
|
+
attr_reader field_name
|
88
|
+
end
|
89
|
+
|
90
|
+
self.field_list << field_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|