relateiq_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+ module RelateIq
2
+ class List
3
+ attr_accessor :id,
4
+ :title,
5
+ :list_type,
6
+ :modified_date,
7
+ :fields
8
+
9
+ def initialize(attrs = {})
10
+ if attrs.key? :listType
11
+ initialize_from_api(attrs)
12
+ else
13
+ initialize_from_user(attrs)
14
+ end
15
+ end
16
+
17
+ def self.resource
18
+ @resource ||= ServiceFactory.get_endpoint('lists')
19
+ end
20
+
21
+ def self.all
22
+ @all ||= from_json(resource.get)
23
+ end
24
+
25
+ def self.find_by_title(title)
26
+ all.find { |l| l.title.downcase == title.downcase }
27
+ end
28
+
29
+ def self.find(id)
30
+ all.find { |l| l.id == id }
31
+ end
32
+
33
+ def self.clean_cache
34
+ @all = nil
35
+ end
36
+
37
+ def self.from_json(json_string)
38
+ lists_hash = JSON.parse(json_string, symbolize_names: true)
39
+ if lists_hash.key? :objects
40
+ lists_hash[:objects].map { |li| List.new(li) }
41
+ else
42
+ List.new(lists_hash)
43
+ end
44
+ end
45
+
46
+ def upsert_item(list_item_hash)
47
+ list_item_hash.merge!(list_id: id)
48
+ list_item_class.new(list_item_hash).save
49
+ end
50
+
51
+ def items_by_contact_id(contact_id)
52
+ list_item_class.find_by_contact(id, contact_id)
53
+ end
54
+
55
+ private
56
+
57
+ def list_item_class
58
+ @list_item_class ||= ListItem
59
+ end
60
+
61
+ def initialize_from_api(attrs)
62
+ @id = attrs.fetch(:id)
63
+ @title = attrs.fetch(:title)
64
+ @fields = attrs.fetch(:fields)
65
+ @list_type = attrs.fetch(:listType)
66
+ @modified_date = attrs.fetch(:modifiedDate)
67
+ end
68
+
69
+ def initialize_from_user(attrs)
70
+ @id = attrs.fetch(:id, nil)
71
+ @title = attrs.fetch(:title, nil)
72
+ @list_type = attrs.fetch(:list_type, nil)
73
+ @modified_date = attrs.fetch(:modified_date, nil)
74
+ @fields = attrs.fetch(:fields, nil)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,107 @@
1
+ module RelateIq
2
+ class ListItem
3
+ attr_accessor :list_id,
4
+ :id,
5
+ :name,
6
+ :account_id,
7
+ :contact_ids,
8
+ :field_values
9
+
10
+ # field values always contains decoded values
11
+ # for example { 'Status' => 'Application Submitted' }
12
+
13
+ def initialize(attrs = {})
14
+ if attrs.key? :listId
15
+ initialize_from_api(attrs)
16
+ else
17
+ initialize_by_user(attrs)
18
+ end
19
+ end
20
+
21
+ def initialize_by_user(attrs)
22
+ @list_id = attrs.fetch(:list_id)
23
+ @id = attrs.fetch(:id, nil)
24
+ @name = attrs.fetch(:name, nil)
25
+ @account_id = attrs.fetch(:account_id, nil)
26
+ @contact_ids = attrs.fetch(:contact_ids, [])
27
+ @field_values = attrs.fetch(:field_values, [])
28
+ end
29
+
30
+ def initialize_from_api(attrs)
31
+ @list_id = attrs.fetch(:listId)
32
+ @id = attrs.fetch(:id, nil)
33
+ @name = attrs.fetch(:name, nil)
34
+ @account_id = attrs.fetch(:accountId, nil)
35
+ @contact_ids = attrs.fetch(:contactIds, [])
36
+ @field_values = decode_field_values(attrs.fetch(:fieldValues, nil))
37
+ end
38
+
39
+ def self.resource(list_id)
40
+ @resource ||= ServiceFactory.get_endpoint('lists')
41
+ @resource["#{list_id}/listitems"]
42
+ end
43
+
44
+ def self.from_json(json_string)
45
+ list_item_hash = JSON.parse(json_string, symbolize_names: true)
46
+ if list_item_hash.key? :objects
47
+ list_item_hash[:objects].map { |li| ListItem.new(li) }
48
+ else
49
+ ListItem.new(list_item_hash)
50
+ end
51
+ end
52
+
53
+ def self.create(attrs)
54
+ ListItem.new(attrs).save
55
+ end
56
+
57
+ def self.find_by_contact(list_id, contact_id)
58
+ from_json(resource(list_id)["?contactIds=#{contact_id}"].get)
59
+ end
60
+
61
+ def save
62
+ if id
63
+ ListItem.from_json(ListItem.resource(list_id)["#{id}"].put to_json)
64
+ else
65
+ ListItem.from_json(ListItem.resource(list_id).post to_json)
66
+ end
67
+ end
68
+
69
+ def to_json
70
+ to_hash.to_json
71
+ end
72
+
73
+ private
74
+
75
+ def to_hash
76
+ result = { 'listId' => list_id }
77
+ result.merge!('id' => id) if id
78
+ result.merge!('name' => name) if name
79
+ result.merge!('accountId' => account_id) if account_id && list.list_type == 'account'
80
+ result.merge!('contactIds' => contact_ids) if contact_ids && contact_ids.count > 0
81
+ result.merge!('fieldValues' => encode_field_values(field_values)) if field_values && field_values.count > 0
82
+ result
83
+ end
84
+
85
+ def encode_field_values(values)
86
+ return [] unless values
87
+ encoder = field_value_encoder.new(list: list)
88
+ encoded_values = {}
89
+ values.each { |fv| encoded_values.merge!(encoder.encode(fv)) }
90
+ encoded_values
91
+ end
92
+
93
+ def decode_field_values(values)
94
+ return [] unless values
95
+ encoder = field_value_encoder.new(list: list)
96
+ values.map { |k, v| encoder.decode(k => v) }
97
+ end
98
+
99
+ def field_value_encoder
100
+ @field_value_encoder ||= RelateIq::Utils::FieldValueEncoder
101
+ end
102
+
103
+ def list
104
+ List.find(list_id)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,12 @@
1
+ module RelateIq
2
+ class ServiceFactory
3
+ def self.get_endpoint(endpoint)
4
+ RestClient::Resource.new(
5
+ "#{RelateIq.configuration.base_url}/#{endpoint}",
6
+ user: RelateIq.configuration.username,
7
+ password: RelateIq.configuration.password,
8
+ headers: { content_type: :json, accept: :json }
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,73 @@
1
+ module RelateIq
2
+ module Utils
3
+ class FieldValueEncoder
4
+ attr_accessor :list
5
+
6
+ def initialize(attrs = {})
7
+ @list = attrs.fetch(:list)
8
+ end
9
+
10
+ def encode(decoded_value)
11
+ return {} if decoded_value.nil?
12
+ name = decoded_value.keys[0]
13
+ values = decoded_value.values[0]
14
+ field = find_field_by_name(name)
15
+ encoded_value = encode_values(field, values)
16
+ return {} if encoded_value == []
17
+ { "#{field[:id]}" => encoded_value }
18
+ end
19
+
20
+ def decode(encoded_value)
21
+ return {} if encoded_value.nil?
22
+ id = encoded_value.keys[0]
23
+ values = encoded_value.values[0]
24
+ field = find_field(id)
25
+ { "#{field[:name]}" => decode_values(field, values) }
26
+ end
27
+
28
+ private
29
+
30
+ def decode_values(field, field_values)
31
+ result = []
32
+ field_values.each { |fv| result << list_option_value(field, fv[:raw], 'id', 'display') }
33
+ result.count > 1 ? result : result[0]
34
+ end
35
+
36
+ def encode_values(field, field_values)
37
+ result = []
38
+ to_array(field_values).each do |fv|
39
+ list_option = list_option_value(field, fv, 'display', 'id')
40
+ result << { 'raw' => list_option } unless list_option.nil?
41
+ end
42
+ result
43
+ end
44
+
45
+ def list_option_value(field, value, key, value_key)
46
+ return nil if value.nil?
47
+ list_options = field[:listOptions]
48
+ if list_options && list_options.count > 0
49
+ option = list_options.find { |lo| lo[key.to_sym].downcase == value.downcase }
50
+ option ||= fail(FieldListOptionNotFoundError, key => value)
51
+ option[value_key.to_sym]
52
+ else
53
+ value
54
+ end
55
+ end
56
+
57
+ def find_field_by_name(name)
58
+ list.fields.find { |f| f[:name] == name } || fail(FieldNotFoundError, name)
59
+ end
60
+
61
+ def find_field(id)
62
+ list.fields.find { |f| f[:id] == id.to_s } || fail(FieldNotFoundError, id)
63
+ end
64
+
65
+ def to_array(value)
66
+ value.is_a?(Array) ? value : [value]
67
+ end
68
+ end
69
+
70
+ class FieldNotFoundError < StandardError; end
71
+ class FieldListOptionNotFoundError < StandardError; end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module RelateIq
2
+ VERSION = "0.1.0"
3
+ end
data/relateiq.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'relateiq/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'relateiq_client'
8
+ s.version = RelateIq::VERSION
9
+ s.authors = ['Dorian Karter']
10
+ s.email = 'jobs@doriankarter.com'
11
+
12
+ s.summary = 'Wrapper client for RelateIQ API'
13
+ s.description = 'Wrapper client for RelateIQ allows using RelateIQ API from ruby'
14
+ s.homepage = 'https://github.com/dkarter/relate_iq_client'
15
+ s.license = 'MIT'
16
+
17
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
19
+ s.bindir = 'bin'
20
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ s.require_paths = [ 'lib' ]
22
+
23
+ s.add_dependency 'rest-client', '~> 1.7'
24
+
25
+ s.add_development_dependency 'bundler', '~> 1.7'
26
+ s.add_development_dependency 'rake', '~> 10.0'
27
+ s.add_development_dependency 'rspec', '~>3.1'
28
+ s.add_development_dependency 'pry', '~> 0.10'
29
+ s.add_development_dependency 'pry-stack_explorer', '~> 0.4.9'
30
+ s.add_development_dependency 'zenflow'
31
+ s.add_development_dependency 'rubocop'
32
+ s.add_development_dependency 'simplecov'
33
+ s.add_development_dependency 'simplecov-json'
34
+ s.add_development_dependency 'cadre'
35
+ s.add_development_dependency 'codeclimate-test-reporter'
36
+ s.add_development_dependency 'webmock'
37
+ end
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "accountid",
3
+ "modifiedDate": 1423507504147,
4
+ "name": "Bolstr, Inc.",
5
+ "fieldValues": {}
6
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "id": "contactid",
3
+ "modifiedDate": 1424713865596,
4
+ "properties": {
5
+ "address": [
6
+ {
7
+ "value": "688 Dink rest, East Lu, Z2K 8OW",
8
+ "metadata": {}
9
+ }
10
+ ],
11
+ "company": [
12
+ {
13
+ "value": "Bolstr",
14
+ "metadata": {}
15
+ },
16
+ {
17
+ "value": "Bolstr, Inc.",
18
+ "metadata": {}
19
+ }
20
+ ],
21
+ "phone": [
22
+ {
23
+ "value": "+44 (0)5426658700",
24
+ "metadata": {}
25
+ }
26
+ ],
27
+ "name": [
28
+ {
29
+ "value": "Kipp Rice",
30
+ "metadata": {}
31
+ }
32
+ ],
33
+ "email": [
34
+ {
35
+ "value": "gulgowski.devyn@example.com",
36
+ "metadata": {}
37
+ }
38
+ ],
39
+ "liurl": [
40
+ {
41
+ "value": "https://www.linkedin.com/in/doriankarter",
42
+ "metadata": {}
43
+ }
44
+ ]
45
+ }
46
+ }
File without changes
@@ -0,0 +1,34 @@
1
+ {
2
+ "objects": [
3
+ {
4
+ "id": "contactid",
5
+ "modifiedDate": 1424713865596,
6
+ "properties": {
7
+ "address": [
8
+ {
9
+ "value": "688 Dink rest, East Lu, Z2K 8OW",
10
+ "metadata": {}
11
+ }
12
+ ],
13
+ "phone": [
14
+ {
15
+ "value": "+44 (0)5426658700",
16
+ "metadata": {}
17
+ }
18
+ ],
19
+ "name": [
20
+ {
21
+ "value": "Kipp Rice",
22
+ "metadata": {}
23
+ }
24
+ ],
25
+ "email": [
26
+ {
27
+ "value": "gulgowski.devyn@example.com",
28
+ "metadata": {}
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "id": "listitemid",
3
+ "listId": "list2id",
4
+ "version": 1,
5
+ "createdDate": 1417755155000,
6
+ "modifiedDate": 1421866566579,
7
+ "name": "Mack Koch",
8
+ "accountId": "accountid",
9
+ "contactIds": [
10
+ "contactid1",
11
+ "contactid2"
12
+ ],
13
+ "fieldValues": {
14
+ "0": [
15
+ {
16
+ "raw": "3"
17
+ }
18
+ ],
19
+ "15": [
20
+ {
21
+ "raw": "Boulder"
22
+ }
23
+ ],
24
+ "25": [
25
+ {
26
+ "raw": "5"
27
+ }
28
+ ],
29
+ "27": [
30
+ {
31
+ "raw": "30000"
32
+ }
33
+ ],
34
+ "40": [
35
+ {
36
+ "raw": "1417755155526"
37
+ }
38
+ ],
39
+ "43": [
40
+ {
41
+ "raw": "ec805c1d1effbc2768ce94108e30eeae74e15931"
42
+ }
43
+ ],
44
+ "44": [
45
+ {
46
+ "raw": "1"
47
+ }
48
+ ],
49
+ "45": [
50
+ {
51
+ "raw": "3"
52
+ }
53
+ ],
54
+ "46": [
55
+ {
56
+ "raw": "18"
57
+ }
58
+ ]
59
+ },
60
+ "linkedItemIds": {
61
+ "5ff139a60e6220c263558fbc0f26786050a70b34.accounts": [
62
+ {
63
+ "itemId": "4d7f9d97f6c00d41fcbfffad489f60f9d8cbfeb1"
64
+ }
65
+ ],
66
+ "list.cd669e1c0fa10a6a98639489807851415d3499d1": [
67
+ {
68
+ "itemId": "d7e41e76a945d9811aa7981fdd6a4ee5c28fdc05"
69
+ }
70
+ ]
71
+ }
72
+ }