contactually-rb 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|