relateiq_client 0.1.0
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/.gitignore +55 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -0
- data/.simplecov +9 -0
- data/.travis.yml +6 -0
- data/.zenflow +11 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +112 -0
- data/Guardfile +17 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/lib/relateiq.rb +31 -0
- data/lib/relateiq/account.rb +43 -0
- data/lib/relateiq/contact.rb +127 -0
- data/lib/relateiq/list.rb +77 -0
- data/lib/relateiq/list_item.rb +107 -0
- data/lib/relateiq/service_factory.rb +12 -0
- data/lib/relateiq/utils/field_value_encoder.rb +73 -0
- data/lib/relateiq/version.rb +3 -0
- data/relateiq.gemspec +37 -0
- data/spec/fixtures/account_by_id.json +6 -0
- data/spec/fixtures/contact.json +46 -0
- data/spec/fixtures/contacts.json +0 -0
- data/spec/fixtures/contacts_by_email.json +34 -0
- data/spec/fixtures/list_item.json +72 -0
- data/spec/fixtures/list_items_by_contact_id.json +37 -0
- data/spec/fixtures/lists.json +1373 -0
- data/spec/integration/relateiq_integration_spec.rb +69 -0
- data/spec/lib/relateiq/account_spec.rb +66 -0
- data/spec/lib/relateiq/contact_spec.rb +161 -0
- data/spec/lib/relateiq/list_item_spec.rb +133 -0
- data/spec/lib/relateiq/list_spec.rb +145 -0
- data/spec/lib/relateiq/service_factory_spec.rb +8 -0
- data/spec/lib/relateiq/utils/field_value_encoder_spec.rb +91 -0
- data/spec/relate_iq_spec.rb +43 -0
- data/spec/spec_helper.rb +44 -0
- metadata +278 -0
@@ -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
|
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,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
|
+
}
|