contactually-rb 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ module Contactually
2
+ class Buckets < Base
3
+ implements :list, :fetch
4
+
5
+ def initialize(url: '/v2/buckets', interface: nil)
6
+ @url = url
7
+ @interface = interface
8
+ end
9
+
10
+ def model
11
+ Models::Bucket
12
+ end
13
+ end
14
+ end
@@ -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,14 @@
1
+ module Contactually
2
+ class Contacts < Base
3
+ implements :list, :fetch
4
+
5
+ def initialize(url: '/v2/contacts', interface: nil)
6
+ @url = url
7
+ @interface = interface
8
+ end
9
+
10
+ def model
11
+ Models::Contact
12
+ end
13
+ end
14
+ 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,18 @@
1
+ module Contactually
2
+ class Me < Base
3
+ implements :fetch
4
+
5
+ def initialize(url: '/v2/me', interface: nil)
6
+ @url = url
7
+ @interface = interface
8
+ end
9
+
10
+ def fetch
11
+ super(nil)
12
+ end
13
+
14
+ def model
15
+ Models::User
16
+ end
17
+ end
18
+ 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,11 @@
1
+ module Contactually
2
+ module Models
3
+ class EmailAddress
4
+ include Model
5
+
6
+ field :id, :string
7
+ field :label, :string
8
+ field :address, :string
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,11 @@
1
+ module Contactually
2
+ module Models
3
+ class PhoneNumber
4
+ include Model
5
+
6
+ field :id, :string
7
+ field :label, :string
8
+ field :number, :string
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Contactually
2
+ module Models
3
+ class SocialMediaProfile
4
+ include Model
5
+
6
+ field :id, :string
7
+ field :label, :string
8
+ field :url, :string
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Contactually
2
+ module Models
3
+ class Tag
4
+ include Model
5
+
6
+ field :contact_count, :integer
7
+ field :created_at, :datetime
8
+ field :id, :string
9
+ field :name, :string
10
+ field :updated_at, :datetime
11
+ end
12
+ end
13
+ end