riq 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +41 -0
- data/.yardopts +2 -0
- data/Gemfile +8 -0
- data/lib/relateiq.rb +3 -0
- data/lib/relateiq/account.rb +79 -0
- data/lib/relateiq/batch_manager.rb +187 -0
- data/lib/relateiq/client.rb +152 -0
- data/lib/relateiq/contact.rb +139 -0
- data/lib/relateiq/error.rb +53 -0
- data/lib/relateiq/events.rb +104 -0
- data/lib/relateiq/list.rb +87 -0
- data/lib/relateiq/list_item_manager.rb +13 -0
- data/lib/relateiq/listitem.rb +115 -0
- data/lib/relateiq/riq_obj.rb +142 -0
- data/lib/relateiq/user.rb +36 -0
- data/test/test.rb +4 -0
- data/test/test_account.rb +39 -0
- data/test/test_batch_manager.rb +45 -0
- data/test/test_contact.rb +50 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 34a510c0f7cad37904ababa9279e498c71e0bb9c
|
4
|
+
data.tar.gz: 661d369470ad7fa0be4588036ab57206f41a4e55
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 71590ee05218590ebb4adc49aebeb462958bf843448d025c466f0b8a53b20ce4b0893d8bf85509790321e8deafc996eda3700ad9a5ad5aa1d8025625d956f386
|
7
|
+
data.tar.gz: c3c788129202330ad448cff886a0981b1a594d4808e806f4f444815aefd5929d990aee56936c4b03b143c7f955b03779f49aace5f6645544ce9a6f2e1aec5e48
|
data/.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Created by https://www.gitignore.io
|
2
|
+
|
3
|
+
### Ruby ###
|
4
|
+
*.gem
|
5
|
+
*.rbc
|
6
|
+
/.config
|
7
|
+
/coverage/
|
8
|
+
/InstalledFiles
|
9
|
+
/pkg/
|
10
|
+
/spec/reports/
|
11
|
+
/test/tmp/
|
12
|
+
/test/version_tmp/
|
13
|
+
/tmp/
|
14
|
+
|
15
|
+
## Specific to RubyMotion:
|
16
|
+
.dat*
|
17
|
+
.repl_history
|
18
|
+
build/
|
19
|
+
|
20
|
+
## Documentation cache and generated files:
|
21
|
+
/.yardoc/
|
22
|
+
/_yardoc/
|
23
|
+
/doc/
|
24
|
+
/rdoc/
|
25
|
+
|
26
|
+
## Environment normalisation:
|
27
|
+
/.bundle/
|
28
|
+
/vendor/bundle
|
29
|
+
/lib/bundler/man/
|
30
|
+
|
31
|
+
# for a library or gem, you might want to ignore these files since the code is
|
32
|
+
# intended to run in multiple environments; otherwise, check them in:
|
33
|
+
# Gemfile.lock
|
34
|
+
# .ruby-version
|
35
|
+
# .ruby-gemset
|
36
|
+
|
37
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
38
|
+
.rvmrc
|
39
|
+
|
40
|
+
# test file
|
41
|
+
t.rb
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/lib/relateiq.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative 'riq_obj'
|
2
|
+
|
3
|
+
module RIQ
|
4
|
+
class Account < RIQObject
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :field_values
|
7
|
+
|
8
|
+
# (see RIQObject#node)
|
9
|
+
def node
|
10
|
+
"accounts/#{@id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# (see #node)
|
14
|
+
def self.node(arg = nil)
|
15
|
+
"accounts"
|
16
|
+
end
|
17
|
+
|
18
|
+
# (see RIQObject#data)
|
19
|
+
def data
|
20
|
+
{
|
21
|
+
id: @id,
|
22
|
+
name: @name,
|
23
|
+
field_values: @field_values
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# (see RIQObject#payload)
|
28
|
+
def payload
|
29
|
+
# TODO: find more elegant way to do this
|
30
|
+
pld = data
|
31
|
+
pld['fieldValues'] = to_raw(@field_values)
|
32
|
+
pld.delete(:field_values)
|
33
|
+
pld.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
# @overload field_value(key)
|
37
|
+
# @param key [String, Integer]
|
38
|
+
# @return [Array] Value of key
|
39
|
+
# @overload field_value(key, value)
|
40
|
+
# Sets key to value
|
41
|
+
# @param key [String, Integer] Key to set
|
42
|
+
# @param value [#to_s] Sets key to value
|
43
|
+
def field_value(key, value = nil)
|
44
|
+
# TODO: double check that this works with arrays of stuff
|
45
|
+
# or, have a format function that casts ints to string on save
|
46
|
+
if value.nil?
|
47
|
+
@field_values.fetch(key.to_s, nil)
|
48
|
+
else
|
49
|
+
@field_values[key.to_s] = value.to_s
|
50
|
+
{key.to_s => value.to_s}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def init(obj = nil)
|
56
|
+
unless obj.nil?
|
57
|
+
@id = obj['id']
|
58
|
+
@name = obj['name']
|
59
|
+
@field_values = from_raw(obj['fieldValues'])
|
60
|
+
else
|
61
|
+
@id = nil
|
62
|
+
@name = nil
|
63
|
+
@field_values = {}
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
# Convenince method to create new Accounts
|
72
|
+
# @param id [String, nil] create a blank Account object or
|
73
|
+
# fetch an existing one by id.
|
74
|
+
# @return [RIQ::Account]
|
75
|
+
def account(id = nil)
|
76
|
+
Account.new(id)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module RIQ
|
2
|
+
# Manages caching and fetching for a certain type of child
|
3
|
+
class BatchManager
|
4
|
+
attr_reader :fetch_options
|
5
|
+
|
6
|
+
# @param klass [RIQObject] The child class that's being fetched, such as {Account} or {ListItem}
|
7
|
+
# @param page_length [Integer] The number of items per page
|
8
|
+
# @!macro [new] krass
|
9
|
+
# is a $1
|
10
|
+
def initialize(klass, page_length = 200, fetch_options: {})
|
11
|
+
@klass = klass
|
12
|
+
begin
|
13
|
+
raise unless @klass.ancestors.include? RIQ::RIQObject
|
14
|
+
rescue
|
15
|
+
raise RIQError, 'Must pass a RIQ Class'
|
16
|
+
end
|
17
|
+
|
18
|
+
reset_cache
|
19
|
+
# ruby says that nil is an argument and page_size was getting set to 0
|
20
|
+
@page_length = page_length.nil? ? 200 : page_length
|
21
|
+
@fetch_options = fetch_options
|
22
|
+
@client = RIQ.client
|
23
|
+
end
|
24
|
+
|
25
|
+
# Iterator for each item in the manager. Pass a block to it!
|
26
|
+
def each(&blok)
|
27
|
+
reset_cache
|
28
|
+
loop do
|
29
|
+
x = next_item
|
30
|
+
if x
|
31
|
+
blok.call(x)
|
32
|
+
else
|
33
|
+
return
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [@macro klass]
|
39
|
+
def first
|
40
|
+
reset_cache
|
41
|
+
fetch_page(0, 1).first
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_options=(opts = {})
|
45
|
+
reset_cache
|
46
|
+
options = {}
|
47
|
+
opts.each do |k, v|
|
48
|
+
# some args are comma-separated arrays, some are just args
|
49
|
+
if v.is_a? Array
|
50
|
+
options[k] = v.join(',')
|
51
|
+
else
|
52
|
+
options[k] = v
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@fetch_options = options
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def next_item
|
61
|
+
if @cache_index == @cache.size
|
62
|
+
return nil if ![0, @page_length].include? @cache.size
|
63
|
+
# puts "\n=== fetching page! (#{@page_index}) (#{@page_length})"
|
64
|
+
@cache = fetch_page(@page_index, @page_length)
|
65
|
+
@page_index += @cache.size
|
66
|
+
@cache_index = 0
|
67
|
+
end
|
68
|
+
|
69
|
+
if @cache_index < @cache.size
|
70
|
+
obj = @cache[@cache_index]
|
71
|
+
@cache_index += 1
|
72
|
+
obj
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# def fetch_batch(param, values, max_size)
|
79
|
+
# objects = []
|
80
|
+
# chunks = []
|
81
|
+
# (0...values.size).step(max_size) do |i|
|
82
|
+
# chunks << values[i...i+max_size]
|
83
|
+
# end
|
84
|
+
# chunks.each_with_index do |obj, i|
|
85
|
+
# begin
|
86
|
+
# set_fetch_options({param: chunk.join(',')})
|
87
|
+
# objects << fetch_page
|
88
|
+
# rescue HTTPError => e
|
89
|
+
# # max url length is 8192 (2^13). If longer, will give a 414 error.
|
90
|
+
# if [413, 414].include? e.code
|
91
|
+
# objects << fetch_batch(param, chunk, (max_size/2.0).ceil)
|
92
|
+
# else
|
93
|
+
# # raise a certain error?
|
94
|
+
# raise e
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# objects
|
99
|
+
# end
|
100
|
+
|
101
|
+
def fetch_page(index = 0, limit = nil)
|
102
|
+
limit = @page_length if limit.nil?
|
103
|
+
|
104
|
+
@fetch_options[:_start] = index
|
105
|
+
@fetch_options[:_limit] = limit
|
106
|
+
|
107
|
+
# all class#node functions can take an arg, so far, only list items need it.
|
108
|
+
# having list_id usually be nil (that is, undefined) is fine. probably.
|
109
|
+
data = @client.get(@klass.node(@list_id), options: @fetch_options)
|
110
|
+
|
111
|
+
objects = []
|
112
|
+
data.fetch('objects', []).each do |obj|
|
113
|
+
objects << @klass.new(obj)
|
114
|
+
end
|
115
|
+
objects
|
116
|
+
end
|
117
|
+
|
118
|
+
def reset_cache
|
119
|
+
# shouldn't reset options, that's silly
|
120
|
+
# @fetch_options = {}
|
121
|
+
@cache = []
|
122
|
+
@cache_index = 0
|
123
|
+
@page_index = 0
|
124
|
+
end
|
125
|
+
|
126
|
+
# these used to be 2 methods, now they're one
|
127
|
+
def batch(objs, mode)
|
128
|
+
return_objs = {}
|
129
|
+
while objs.size > 0
|
130
|
+
chunk_size = objs.size > @page_length ? @page_length : objs.size
|
131
|
+
chunk = objs[0...chunk_size]
|
132
|
+
objs = chunk_size < objs.size ? objs[chunk_size..-1] : []
|
133
|
+
|
134
|
+
batch_objs = batch_helper(mode, chunk)
|
135
|
+
return_objs['success_objects'] = batch_objs.fetch('success_objects', [])
|
136
|
+
return_objs['error_objects'] = batch_objs.fetch('error_objects', [])
|
137
|
+
end
|
138
|
+
return_objs
|
139
|
+
end
|
140
|
+
|
141
|
+
def batch_helper(mode, objs)
|
142
|
+
payloads = []
|
143
|
+
objs.each {|o| payloads << o.payload}
|
144
|
+
|
145
|
+
if mode == :create
|
146
|
+
batch_results = @client.post('/createBatch', {objects: payloads})
|
147
|
+
elsif mode == :update
|
148
|
+
batch_results = @client.put('/updateBatch', {objects: payloads})
|
149
|
+
else
|
150
|
+
raise RIQError, 'Invalid Batch Mode'
|
151
|
+
end
|
152
|
+
|
153
|
+
batch_results['success_objects'] = []
|
154
|
+
batch_results['successObjects'].each do |o|
|
155
|
+
batch_results['success_objects'] << self.class(o)
|
156
|
+
end
|
157
|
+
|
158
|
+
batch_results['error_objects'] = []
|
159
|
+
batch_results['errorObjects'].each do |o|
|
160
|
+
batch_results['error_objects'] << self.class(o)
|
161
|
+
end
|
162
|
+
|
163
|
+
batch_results
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class << self
|
168
|
+
# @!macro [new] conv
|
169
|
+
# Returns an iterator for all $0
|
170
|
+
# @return [BatchManager]
|
171
|
+
|
172
|
+
# @macro conv
|
173
|
+
def lists(page_size = nil)
|
174
|
+
BatchManager.new(RIQ::List, page_size)
|
175
|
+
end
|
176
|
+
|
177
|
+
# @macro conv
|
178
|
+
def contacts(page_size = nil)
|
179
|
+
BatchManager.new(RIQ::Contact, page_size)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @macro conv
|
183
|
+
def accounts(page_size = nil)
|
184
|
+
BatchManager.new(RIQ::Account, page_size)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'pry'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative 'error'
|
6
|
+
|
7
|
+
module RIQ
|
8
|
+
# HTTP client responsible for actually handling the HTTP requests.
|
9
|
+
# @note Utility class to perform http requests. This shouldn't be
|
10
|
+
# instantiated directy but used by children of RIQObject instead.
|
11
|
+
class Client
|
12
|
+
attr_accessor :cache
|
13
|
+
|
14
|
+
def initialize(key, secret)
|
15
|
+
raise 'Missing credentials' if key.nil? || secret.nil?
|
16
|
+
|
17
|
+
@root_url = 'https://api.relateiq.com/v2'
|
18
|
+
|
19
|
+
@auth = {username: key, password: secret}
|
20
|
+
@headers = {
|
21
|
+
'Content-type' => 'application/json',
|
22
|
+
'Accept' => 'application/json'
|
23
|
+
}
|
24
|
+
@cache = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# for caching, used with #next ?
|
28
|
+
# def store(endpoint, objects)
|
29
|
+
# @cache[endpoint] = objects unless objects.nil?
|
30
|
+
# end
|
31
|
+
|
32
|
+
# Makes a GET request, used for fetching existing objects.
|
33
|
+
# @return (see #request)
|
34
|
+
def get(endpoint, options: nil)
|
35
|
+
request(endpoint, :get, nil, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Makes a POST request, used for creating new objects.
|
39
|
+
# @return (see #request)
|
40
|
+
def post(endpoint, body, options: nil)
|
41
|
+
request(endpoint, :post, body, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Makes a PUT request, used for updating existing objects.
|
45
|
+
# @return (see #request)
|
46
|
+
def put(endpoint, body, options: nil)
|
47
|
+
request(endpoint, :put, body, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Makes a DELETE request, used for deleting existing objects.
|
51
|
+
# @return (see #request)
|
52
|
+
def delete(endpoint, options: nil)
|
53
|
+
request(endpoint, :delete, nil, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch(endpoint = nil, options: {})
|
57
|
+
# i'm not sure why this would return an empty object instead of raising a notfound
|
58
|
+
# or really, why it exists at all
|
59
|
+
# puts "fetching #{endpoint} with options #{options}"
|
60
|
+
# begin
|
61
|
+
get(endpoint, options: options)
|
62
|
+
# rescue NotFoundError
|
63
|
+
# {}
|
64
|
+
# end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# actually does the requesting
|
70
|
+
# @return (see #process_response)
|
71
|
+
def request(endpoint, method = :get, body = nil, options = nil)
|
72
|
+
# there may be a better way to do this, but this is close
|
73
|
+
url = "#{@root_url}/#{endpoint}"
|
74
|
+
# pp "#{method} request to #{url} with body: #{body} and options: #{options}"
|
75
|
+
if [:get, :delete].include? method
|
76
|
+
resp = HTTParty.method(method).call(
|
77
|
+
url,
|
78
|
+
headers: @headers,
|
79
|
+
basic_auth: @auth,
|
80
|
+
query: options
|
81
|
+
)
|
82
|
+
elsif [:post, :put].include? method
|
83
|
+
resp = HTTParty.method(method).call(
|
84
|
+
url,
|
85
|
+
headers: @headers,
|
86
|
+
basic_auth: @auth,
|
87
|
+
query: options,
|
88
|
+
body: body
|
89
|
+
)
|
90
|
+
else
|
91
|
+
# this shouldn't ever get hit?
|
92
|
+
raise RIQError, 'Invalid method'
|
93
|
+
end
|
94
|
+
|
95
|
+
# HTTParty response object
|
96
|
+
process_response(resp)
|
97
|
+
# resp
|
98
|
+
end
|
99
|
+
|
100
|
+
# Checks for errors and returns the parsed object
|
101
|
+
# @return [RIQObject] The result of the request
|
102
|
+
def process_response(resp)
|
103
|
+
# pp "processing #{resp}, code: #{resp.code}"
|
104
|
+
# binding.pry
|
105
|
+
if resp.code == 503
|
106
|
+
raise NotImplementedError, 'This function is not currently supported by RelateIQ'
|
107
|
+
elsif resp.code == 404
|
108
|
+
raise NotFoundError, 'Object Not Found'
|
109
|
+
elsif resp.code >= 400
|
110
|
+
# Record Response Data
|
111
|
+
raise HTTPError, resp
|
112
|
+
end
|
113
|
+
# 404 Object not found
|
114
|
+
# if List, have them check if it is shared
|
115
|
+
# 500
|
116
|
+
# 422
|
117
|
+
# 400 Bad Request - pass on internal message
|
118
|
+
# 502 Bad Gateway - Reattempt
|
119
|
+
|
120
|
+
# if resp.include? 'objects'
|
121
|
+
# resp['objects']
|
122
|
+
# else
|
123
|
+
resp.parsed_response
|
124
|
+
# end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Configuration Methods
|
128
|
+
# def fetchConfig
|
129
|
+
# return get('configs')
|
130
|
+
# end
|
131
|
+
end
|
132
|
+
|
133
|
+
class << self
|
134
|
+
# A singleton client for use by methods in Object.
|
135
|
+
# Always use RIQ.client to retrieve the client object.
|
136
|
+
# @param key [String] RelateIQ API key
|
137
|
+
# @param secret [String] RelateIQ API secret
|
138
|
+
# @return [RIQ] The module, in case you want it.
|
139
|
+
def init(key = nil, secret = nil)
|
140
|
+
key ||= ENV['RIQ_TEST_API_KEY']
|
141
|
+
secret ||= ENV['RIQ_TEST_API_SECRET']
|
142
|
+
|
143
|
+
@@client = Client.new(key, secret)
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
def client
|
148
|
+
raise RIQError, 'Client not initialized' unless @@client
|
149
|
+
@@client
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|