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.
@@ -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