pipekit 0.2.0 → 1.0.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 +4 -4
- data/lib/pipekit/config.rb +105 -0
- data/lib/pipekit/deal.rb +13 -2
- data/lib/pipekit/deal_field.rb +6 -0
- data/lib/pipekit/field_repository.rb +25 -0
- data/lib/pipekit/person.rb +13 -2
- data/lib/pipekit/person_field.rb +1 -10
- data/lib/pipekit/repository.rb +22 -15
- data/lib/pipekit/request.rb +76 -23
- data/lib/pipekit/response.rb +122 -0
- data/lib/pipekit/result.rb +82 -0
- data/lib/pipekit/version.rb +1 -1
- data/lib/pipekit.rb +7 -14
- data/pipekit.gemspec +2 -2
- metadata +12 -22
- data/lib/pipekit/configurable.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a97ca3462a69b58e56337a81273147db6d6b81ca
|
4
|
+
data.tar.gz: a58a9d65ecd33575e907d124a69670692843e8e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 09e9a02a4d5021799ca0cf39a96b55d7c0b5a136c8f0427428037f395034ce203c575421fbacc36559198676fce62f3c3f24ed6b07b99cb8594ad4173d3b8f0b
|
7
|
+
data.tar.gz: 649b62d3e62ae41873880705282876663943b8e0ce38659482814fde8db3140ba8617bdf54a9905b2888679e0be71cdeb6cf7d05d0fc9862b361a32994f3b70b
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Pipekit
|
2
|
+
class Config
|
3
|
+
class << self
|
4
|
+
|
5
|
+
attr_writer :file_path
|
6
|
+
|
7
|
+
# Finds the field name in the config from the Pipedrive ID
|
8
|
+
#
|
9
|
+
# Example
|
10
|
+
#
|
11
|
+
# Config.field_name(:person, "asbasdfasc2343443")
|
12
|
+
# # => "middle_name"
|
13
|
+
#
|
14
|
+
# Config.field_name(:person, "name")
|
15
|
+
# # => "name"
|
16
|
+
def field_name(resource, key)
|
17
|
+
custom_fields(resource)
|
18
|
+
.invert
|
19
|
+
.fetch(key.to_s, key.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Finds the Pipedrive field ID from the config
|
23
|
+
#
|
24
|
+
# Example
|
25
|
+
#
|
26
|
+
# Config.field_id(:person, "middle_name")
|
27
|
+
# # => "asbasdfasc2343443"
|
28
|
+
#
|
29
|
+
# Config.field_id(:person, "name")
|
30
|
+
# # => "name"
|
31
|
+
def field_id(resource, key)
|
32
|
+
custom_fields(resource)
|
33
|
+
.fetch(key.to_s, key.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Finds the Pipedrive field value from the config
|
37
|
+
# translating from a Pipedrive ID in the config if one exists for that
|
38
|
+
# field/value
|
39
|
+
#
|
40
|
+
# Example
|
41
|
+
#
|
42
|
+
# Config.field_value(:person, "inteview_quality", 66)
|
43
|
+
# # => "Amazing"
|
44
|
+
#
|
45
|
+
# Config.field_value(:person, "inteview_quality", "value_not_there")
|
46
|
+
# # => "value_not_there"
|
47
|
+
def field_value(resource, field, value)
|
48
|
+
custom_field_values(resource, field)
|
49
|
+
.fetch(value, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Finds the Pipedrive field value ID from the config if one exists for that
|
53
|
+
# field/value
|
54
|
+
#
|
55
|
+
# Example
|
56
|
+
#
|
57
|
+
# Config.field_value_id(:person, "inteview_quality", "Amazing")
|
58
|
+
# # => 66
|
59
|
+
#
|
60
|
+
# Config.field_value_id(:person, "inteview_quality", "value_not_there")
|
61
|
+
# # => "value_not_there"
|
62
|
+
def field_value_id(resource, field, value)
|
63
|
+
custom_field_values(resource, field)
|
64
|
+
.invert
|
65
|
+
.fetch(value, value)
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch(key, default = nil)
|
69
|
+
config.fetch(key.to_s, default)
|
70
|
+
end
|
71
|
+
|
72
|
+
def custom_fields(resource)
|
73
|
+
fetch("fields", {})
|
74
|
+
.fetch(resource.to_s, {})
|
75
|
+
end
|
76
|
+
|
77
|
+
def custom_field_values(resource, field)
|
78
|
+
fetch("field_values", {})
|
79
|
+
.fetch(resource.to_s, {})
|
80
|
+
.fetch(field.to_s, {})
|
81
|
+
end
|
82
|
+
|
83
|
+
def file_path
|
84
|
+
@file_path || raise_config_error
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def config
|
90
|
+
@config ||= load_config
|
91
|
+
end
|
92
|
+
|
93
|
+
def raise_config_error
|
94
|
+
raise NotSetError, "You need to create a yaml file with your Pipedrive config and set the path to the file using `Pipekit.config_file_path = 'path/to/file.yml'`"
|
95
|
+
end
|
96
|
+
|
97
|
+
def load_config
|
98
|
+
yaml = ERB.new(File.read(file_path)).result
|
99
|
+
YAML.load(yaml)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
NotSetError = Class.new(Exception)
|
104
|
+
end
|
105
|
+
end
|
data/lib/pipekit/deal.rb
CHANGED
@@ -2,8 +2,19 @@ module Pipekit
|
|
2
2
|
class Deal
|
3
3
|
include Repository
|
4
4
|
|
5
|
-
def get_by_person_id(person_id)
|
6
|
-
|
5
|
+
def get_by_person_id(person_id, person_repo: Person.new)
|
6
|
+
raise UnknownPersonError, "No person ID supplied when getting deals by person ID" unless person_id
|
7
|
+
person_repo.find_deals(person_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Finds a person by their email, then finds the first deal related to that
|
11
|
+
# person and updates it with the params provided
|
12
|
+
def update_by_person(email, params, person_repo: Person.new)
|
13
|
+
person = person_repo.find_by(email: email)
|
14
|
+
deal = get_by_person_id(person[:id], person_repo: person_repo).first
|
15
|
+
update(deal[:id], params)
|
7
16
|
end
|
8
17
|
end
|
18
|
+
|
19
|
+
UnknownPersonError = Class.new(StandardError)
|
9
20
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Pipekit
|
2
|
+
module FieldRepository
|
3
|
+
def get_by_key(key)
|
4
|
+
key = Config.field_id(parent_resource, key)
|
5
|
+
search_fields("key", key)
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_by_name(name)
|
9
|
+
search_fields("name", name)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def search_fields(field_element, value)
|
15
|
+
result = request.get.select { |element| element[field_element] == value }
|
16
|
+
|
17
|
+
raise ResourceNotFoundError.new("#{parent_resource}Field searching by element #{field_element} for #{value} could not be found") if result.empty?
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def parent_resource
|
22
|
+
resource.chomp("Field")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/pipekit/person.rb
CHANGED
@@ -3,11 +3,22 @@ module Pipekit
|
|
3
3
|
include Repository
|
4
4
|
|
5
5
|
def get_by_email(email)
|
6
|
-
request.get("
|
6
|
+
request.get("find", term: email, search_by_email: 1)
|
7
7
|
end
|
8
8
|
|
9
9
|
def get_by_name(name)
|
10
|
-
request.get("
|
10
|
+
request.get("find", term: name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_or_update(fields)
|
14
|
+
person = find_by(email: fields[:email])
|
15
|
+
update(person["id"], fields)
|
16
|
+
rescue ResourceNotFoundError
|
17
|
+
create(fields)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_deals(id)
|
21
|
+
request.get("#{id}/deals")
|
11
22
|
end
|
12
23
|
end
|
13
24
|
end
|
data/lib/pipekit/person_field.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
1
|
module Pipekit
|
2
2
|
class PersonField
|
3
3
|
include Repository
|
4
|
-
|
5
|
-
def get_by_id(id)
|
6
|
-
request.get("/#{uri}").select { |element| element["id"] == id }
|
7
|
-
end
|
8
|
-
|
9
|
-
def get_by_key(key)
|
10
|
-
custom_field_key = request.config["people_fields"][key] rescue nil
|
11
|
-
key = custom_field_key || key
|
12
|
-
request.get("/#{uri}").select { |element| element["key"] == key }
|
13
|
-
end
|
4
|
+
include FieldRepository
|
14
5
|
end
|
15
6
|
end
|
data/lib/pipekit/repository.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
class ConfigNotSetError < Exception; end
|
2
1
|
module Pipekit
|
3
2
|
module Repository
|
4
3
|
|
5
|
-
def initialize(request =
|
6
|
-
@request = request
|
4
|
+
def initialize(request = nil)
|
5
|
+
@request = request || Request.new(resource)
|
7
6
|
end
|
8
7
|
|
9
8
|
def all
|
10
|
-
|
9
|
+
get
|
11
10
|
end
|
12
11
|
|
13
12
|
# Public: Get all records from Pipedrive by **one** of the record's fields.
|
@@ -21,8 +20,11 @@ module Pipekit
|
|
21
20
|
# where(id: 123)
|
22
21
|
#
|
23
22
|
# Returns array of Hashes.
|
24
|
-
def where(options)
|
23
|
+
def where(options, raise_error = false)
|
25
24
|
send("get_by_#{options.keys.first}", options.values.first)
|
25
|
+
rescue ResourceNotFoundError => error
|
26
|
+
raise error if raise_error
|
27
|
+
[]
|
26
28
|
end
|
27
29
|
|
28
30
|
# Public: Get the first record by **one** field from Pipedrive.
|
@@ -37,7 +39,7 @@ module Pipekit
|
|
37
39
|
#
|
38
40
|
# Returns a Hash or nil if none found.
|
39
41
|
def find_by(options)
|
40
|
-
where(options).first
|
42
|
+
where(options, true).first
|
41
43
|
end
|
42
44
|
|
43
45
|
# Public: Create a record on Pipedrive.
|
@@ -50,7 +52,7 @@ module Pipekit
|
|
50
52
|
#
|
51
53
|
# Returns nothing.
|
52
54
|
def create(fields)
|
53
|
-
request.post(
|
55
|
+
request.post(fields)
|
54
56
|
end
|
55
57
|
|
56
58
|
# Public: Updates a record on Pipedrive.
|
@@ -63,15 +65,17 @@ module Pipekit
|
|
63
65
|
#
|
64
66
|
# Returns nothing.
|
65
67
|
def update(id, fields)
|
66
|
-
request.put(
|
68
|
+
request.put(id, fields)
|
67
69
|
end
|
68
70
|
|
69
71
|
def self.included(base)
|
70
|
-
base.
|
72
|
+
base.extend(ClassMethods)
|
71
73
|
end
|
72
74
|
|
73
75
|
module ClassMethods
|
74
|
-
|
76
|
+
def resource
|
77
|
+
to_s.split("::").last.tap { |name| name[0] = name[0].downcase }
|
78
|
+
end
|
75
79
|
end
|
76
80
|
|
77
81
|
private
|
@@ -85,18 +89,21 @@ module Pipekit
|
|
85
89
|
get_by_field(field: field, value: args[0])
|
86
90
|
end
|
87
91
|
|
92
|
+
def get(id = nil)
|
93
|
+
request.get(id)
|
94
|
+
end
|
95
|
+
|
88
96
|
def get_by_id(id)
|
89
|
-
[
|
97
|
+
[get(id)]
|
90
98
|
end
|
91
99
|
|
92
100
|
def get_by_field(field:, value:)
|
93
|
-
result = request.search_by_field(
|
101
|
+
result = request.search_by_field(field: field, value: value)
|
94
102
|
result.map { |item| get_by_id(item["id"]) }.flatten
|
95
103
|
end
|
96
104
|
|
97
|
-
def
|
98
|
-
|
99
|
-
"#{class_name.downcase}s"
|
105
|
+
def resource
|
106
|
+
self.class.resource
|
100
107
|
end
|
101
108
|
end
|
102
109
|
end
|
data/lib/pipekit/request.rb
CHANGED
@@ -5,10 +5,16 @@ module Pipekit
|
|
5
5
|
include HTTParty
|
6
6
|
|
7
7
|
PIPEDRIVE_URL = "https://api.pipedrive.com/v1"
|
8
|
+
DEFAULT_PAGINATION_LIMIT = 500
|
8
9
|
|
9
10
|
base_uri PIPEDRIVE_URL
|
10
11
|
format :json
|
11
12
|
|
13
|
+
def initialize(resource)
|
14
|
+
@resource = resource
|
15
|
+
self.class.debug_output $stdout if Config.fetch("debug_requests")
|
16
|
+
end
|
17
|
+
|
12
18
|
# Public: Pipedrive /searchField API call.
|
13
19
|
#
|
14
20
|
# type - Type of the field:
|
@@ -20,50 +26,97 @@ module Pipekit
|
|
20
26
|
#
|
21
27
|
# Examples
|
22
28
|
#
|
23
|
-
# search_by_field(
|
24
|
-
# search_by_field(
|
29
|
+
# search_by_field(field: :cohort, value: 119)
|
30
|
+
# search_by_field(field: :github_username, value: "octocat")
|
25
31
|
#
|
26
|
-
# Returns an array of
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
# Returns an array of Response objects or throws a ResourceNotFoundError if
|
33
|
+
# it couldn't find anything.
|
34
|
+
def search_by_field(field:, value:)
|
35
|
+
query = {field_type: "#{resource}Field",
|
36
|
+
field_key: Config.field_id(resource, field),
|
37
|
+
return_item_ids: true,
|
38
|
+
term: value
|
39
|
+
}
|
31
40
|
|
32
|
-
get("/searchResults/field", options
|
41
|
+
response_from self.class.get("/searchResults/field", options(query: query))
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
|
44
|
+
# Public: Pipedrive GET API call - does a GET request to the Pipedrive API
|
45
|
+
# based on the resource passed in the initialiser
|
46
|
+
#
|
47
|
+
# id - If the resource being searched for has an id
|
48
|
+
# query - An optional query string
|
49
|
+
# start - The offset with which to start the query
|
50
|
+
#
|
51
|
+
# As long as "request_all_pages" is not set to false in the config this will
|
52
|
+
# recursively call `#get` until all the pages of the request have been
|
53
|
+
# fetched from pipedrive
|
54
|
+
# Pipedrive until everything available has been received
|
55
|
+
def get(id = nil, query = {})
|
56
|
+
_get(id, query, get_request(id, query))
|
37
57
|
end
|
38
58
|
|
39
|
-
def put(
|
40
|
-
|
59
|
+
def put(id, data)
|
60
|
+
response_from self.class.put(uri(id), options(body: data))
|
41
61
|
end
|
42
62
|
|
43
|
-
def post(
|
44
|
-
|
63
|
+
def post(data)
|
64
|
+
response_from self.class.post(uri, options(body: data))
|
45
65
|
end
|
46
66
|
|
47
67
|
private
|
48
68
|
|
49
|
-
|
50
|
-
|
69
|
+
attr_reader :resource
|
70
|
+
|
71
|
+
def _get(id, query, result)
|
72
|
+
return result.response(resource) unless result.fetch_next_request?
|
73
|
+
_get(id, query, result + get_request(id, query, result.next_start))
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_request(id, query, start = 0)
|
77
|
+
response = self.class.get(uri(id), options(query: {limit: pagination_limit, start: start}.merge(query)))
|
78
|
+
Result.new(response)
|
51
79
|
end
|
52
80
|
|
53
|
-
def
|
54
|
-
|
55
|
-
response.parsed_response["data"]
|
81
|
+
def response_from(response_data)
|
82
|
+
Result.response(resource, response_data)
|
56
83
|
end
|
57
84
|
|
58
|
-
def
|
59
|
-
|
85
|
+
def uri(id = nil)
|
86
|
+
"/#{resource}s/#{id}".chomp("/")
|
60
87
|
end
|
61
88
|
|
62
89
|
def options(query: {}, body: {})
|
63
90
|
{
|
64
|
-
query:
|
65
|
-
body: body
|
91
|
+
query: query.merge(api_token: Config.fetch("api_token")),
|
92
|
+
body: parse_body(body)
|
66
93
|
}
|
67
94
|
end
|
95
|
+
|
96
|
+
# Replaces custom fields with their Pipedrive ID
|
97
|
+
# if the ID is defined in the configuration
|
98
|
+
#
|
99
|
+
# So if the body looked like this with a custom field
|
100
|
+
# called middle_name:
|
101
|
+
#
|
102
|
+
# { middle_name: "Dave" }
|
103
|
+
#
|
104
|
+
# And it has a Pipedrive ID ("123abc"), this will put in this custom ID
|
105
|
+
#
|
106
|
+
# { "123abc": "Dave" }
|
107
|
+
#
|
108
|
+
# meaning you don't have to worry about the custom IDs
|
109
|
+
def parse_body(body)
|
110
|
+
body.reduce({}) do |result, (field, value)|
|
111
|
+
field = Config.field_id(resource, field)
|
112
|
+
value = Config.field_value_id(resource, field, value)
|
113
|
+
result.tap { |result| result[field] = value }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def pagination_limit
|
118
|
+
Config.fetch("pagination_limit", DEFAULT_PAGINATION_LIMIT)
|
119
|
+
end
|
120
|
+
|
68
121
|
end
|
69
122
|
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Pipekit
|
2
|
+
class Response
|
3
|
+
|
4
|
+
def initialize(resource, data)
|
5
|
+
@resource = resource
|
6
|
+
@data = data || {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
return false unless other.respond_to?(:to_h)
|
11
|
+
to_h == other.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
fetch(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
data.inject({}) do |result, (field, value)|
|
20
|
+
field_name = Config.field_name(resource, field)
|
21
|
+
result[field_name.to_sym] = Config.field_value(resource, field, value)
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :to_hash, :to_h
|
27
|
+
|
28
|
+
# This is more complicated than it first seems as Pipedrive returns any
|
29
|
+
# custom field you might create (such as a new cohort in the Cohort field)
|
30
|
+
# as a meaningless Pipedrive ID so it returns something semantically
|
31
|
+
# meaningnless such as "8" when it means "April 2016"
|
32
|
+
#
|
33
|
+
# There are two ways this method gets around this to bring back the
|
34
|
+
# semantically meaningful result you're looking for here, if you put in the
|
35
|
+
# config under "field_values" the IDs that Pipedrive assigns to your custom
|
36
|
+
# values (you'll have to search the API to work out what these are) it will
|
37
|
+
# look them up there.
|
38
|
+
#
|
39
|
+
# Otherwise you can plass the "find_value_on_pipedrive" flag and it will do
|
40
|
+
# a call to the Pipedrive API to look it up. This is off by default as it is
|
41
|
+
# obviously quite slow to call an API each time you want to fetch some data
|
42
|
+
#
|
43
|
+
# Options:
|
44
|
+
#
|
45
|
+
# find_value_on_pipedrive (default: false) - if set to true will look up using the Pipedrive
|
46
|
+
# API the actual value of field (rather than the Pipedrive ID)
|
47
|
+
#
|
48
|
+
# choose_first_value (default: true) - if Pipedrive returns an array of values this will
|
49
|
+
# choose the first one rather than return the array
|
50
|
+
#
|
51
|
+
# Examples:
|
52
|
+
#
|
53
|
+
# Normally you can just use the square brackets alias to fetch responses as
|
54
|
+
# though this was a hash:
|
55
|
+
#
|
56
|
+
# response[:resource] # returns: "Dave"
|
57
|
+
#
|
58
|
+
# However if you find when doing this Pipedrive returns its meaningless ID
|
59
|
+
#
|
60
|
+
# response[:cohort] # returns: 1234
|
61
|
+
#
|
62
|
+
# then you can tell Pipedrive to fetch it manually
|
63
|
+
#
|
64
|
+
# response.fetch(:cohort, find_value_on_pipedrive: true) # returns: "April
|
65
|
+
# 2016"
|
66
|
+
#
|
67
|
+
def fetch(key, default = nil, opts = {})
|
68
|
+
opts = {
|
69
|
+
find_value_on_pipedrive: false,
|
70
|
+
choose_first_value: true
|
71
|
+
}.merge(opts)
|
72
|
+
|
73
|
+
value = fetch_value(key, default)
|
74
|
+
|
75
|
+
return value_from_pipedrive(key.to_s, value) if opts[:find_value_on_pipedrive]
|
76
|
+
convert_value(key, value, opts)
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_key?(key)
|
80
|
+
data.has_key? convert_key(key)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
attr_reader :data, :resource
|
86
|
+
|
87
|
+
def fetch_value(key, default)
|
88
|
+
data.fetch(convert_key(key), default)
|
89
|
+
end
|
90
|
+
|
91
|
+
def value_from_pipedrive(key, value)
|
92
|
+
return unless value && !value.empty?
|
93
|
+
|
94
|
+
option = field_repository
|
95
|
+
.find_by(name: key)
|
96
|
+
.fetch("options", [], choose_first_value: false)
|
97
|
+
.find { |options| options["id"] == value.to_i }
|
98
|
+
|
99
|
+
raise ResourceNotFoundError.new("Could not find field #{key}'s value '#{value}' on Pipedrive") unless option
|
100
|
+
option.fetch("label")
|
101
|
+
end
|
102
|
+
|
103
|
+
def field_repository
|
104
|
+
Object.const_get("Pipekit::#{resource.capitalize}Field").new
|
105
|
+
end
|
106
|
+
|
107
|
+
def convert_key(key)
|
108
|
+
Config.field_id(resource, key)
|
109
|
+
end
|
110
|
+
|
111
|
+
def convert_value(key, value, opts)
|
112
|
+
value = choose_first(value) if opts[:choose_first_value]
|
113
|
+
Config.field_value(resource, key, value)
|
114
|
+
end
|
115
|
+
|
116
|
+
def choose_first(result)
|
117
|
+
return result unless result.is_a? Array
|
118
|
+
return if result.empty?
|
119
|
+
result.first["value"]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Understands how to represent the result of a request to the Pipedrive API
|
2
|
+
module Pipekit
|
3
|
+
class Result
|
4
|
+
|
5
|
+
def initialize(response_data)
|
6
|
+
@response_data = response_data
|
7
|
+
raise UnsuccessfulRequestError.new(response_data) unless success?
|
8
|
+
end
|
9
|
+
|
10
|
+
def response(resource)
|
11
|
+
raise ResourceNotFoundError.new(response_data) unless resource_found?
|
12
|
+
return Response.new(resource, response_body) unless response_body.is_a? Array
|
13
|
+
response_body.map { |data| Response.new(resource, data) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def +(other)
|
17
|
+
self.class.new(other.merged_response(response_body))
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_next_request?
|
21
|
+
Config.fetch("request_all_pages", true) && pagination_data["more_items_in_collection"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def next_start
|
25
|
+
pagination_data["next_start"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.response(resource, response_data)
|
29
|
+
new(response_data).response(resource)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def merged_response(other_body)
|
35
|
+
response_data.tap do |response|
|
36
|
+
response["data"] = other_body + response_body
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :response_data
|
43
|
+
|
44
|
+
def pagination_data
|
45
|
+
response_data
|
46
|
+
.fetch("additional_data", {})
|
47
|
+
.fetch("pagination", {})
|
48
|
+
end
|
49
|
+
|
50
|
+
def response_body
|
51
|
+
response_data["data"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def success?
|
55
|
+
response_data["success"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def resource_found?
|
59
|
+
!(response_body.nil? || response_body.empty?)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ResourceNotFoundError < StandardError
|
64
|
+
def initialize(response)
|
65
|
+
@response = response
|
66
|
+
end
|
67
|
+
|
68
|
+
def message
|
69
|
+
"Resource not found: #{@response}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class UnsuccessfulRequestError < StandardError
|
74
|
+
def initialize(response)
|
75
|
+
@response = response
|
76
|
+
end
|
77
|
+
|
78
|
+
def message
|
79
|
+
"Request not successful: #{@response}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/pipekit/version.rb
CHANGED
data/lib/pipekit.rb
CHANGED
@@ -1,33 +1,26 @@
|
|
1
|
-
require "pipekit/
|
1
|
+
require "pipekit/config"
|
2
2
|
require "httparty"
|
3
3
|
require "pipekit/version"
|
4
4
|
require "pipekit/request"
|
5
|
+
require "pipekit/result"
|
6
|
+
require "pipekit/response"
|
5
7
|
require "pipekit/repository"
|
8
|
+
require "pipekit/field_repository"
|
6
9
|
require "pipekit/person"
|
7
10
|
require "pipekit/deal"
|
8
11
|
require "pipekit/person_field"
|
12
|
+
require "pipekit/deal_field"
|
9
13
|
require "pipekit/note"
|
10
14
|
|
11
15
|
module Pipekit
|
12
|
-
include Configurable
|
13
16
|
|
14
17
|
# Define a path to Pipedrive config file
|
15
18
|
#
|
16
19
|
# Example:
|
17
20
|
#
|
18
21
|
# Pipekit.config_file_path = File.join("config", "pipedrive.yml")
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def config_file_path
|
23
|
-
@config_file_path || raise_config_error
|
24
|
-
end
|
25
|
-
|
26
|
-
def raise_config_error
|
27
|
-
raise ConfigNotSetError, "You need to create a yaml file with your Pipedrive config and set the path to the file using `Pipedrive.config_file_path = 'path/to/file.yml'`"
|
28
|
-
end
|
22
|
+
def self.config_file_path=(path)
|
23
|
+
Config.file_path = path
|
29
24
|
end
|
30
|
-
|
31
|
-
ConfigNotSetError = Class.new(Exception)
|
32
25
|
end
|
33
26
|
|
data/pipekit.gemspec
CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.description = %q{Pipedrive API client for Ruby.}
|
14
14
|
spec.homepage = "https://github.com/makersacademy/pipekit"
|
15
15
|
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = '>= 1.9.1'
|
16
17
|
|
17
18
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
19
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
@@ -28,11 +29,10 @@ Gem::Specification.new do |spec|
|
|
28
29
|
spec.require_paths = ["lib"]
|
29
30
|
|
30
31
|
spec.add_dependency "httparty"
|
31
|
-
spec.add_dependency "activesupport"
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 1.12"
|
34
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
35
|
spec.add_development_dependency "rspec", "~> 3.0"
|
36
|
-
spec.add_development_dependency "webmock"
|
36
|
+
spec.add_development_dependency "webmock", "~> 2.1.0"
|
37
37
|
spec.add_development_dependency "pry"
|
38
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pipekit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jafrog
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-08-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httparty
|
@@ -26,20 +26,6 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: activesupport
|
31
|
-
requirement: !ruby/object:Gem::Requirement
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: '0'
|
36
|
-
type: :runtime
|
37
|
-
prerelease: false
|
38
|
-
version_requirements: !ruby/object:Gem::Requirement
|
39
|
-
requirements:
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '0'
|
43
29
|
- !ruby/object:Gem::Dependency
|
44
30
|
name: bundler
|
45
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,16 +72,16 @@ dependencies:
|
|
86
72
|
name: webmock
|
87
73
|
requirement: !ruby/object:Gem::Requirement
|
88
74
|
requirements:
|
89
|
-
- - "
|
75
|
+
- - "~>"
|
90
76
|
- !ruby/object:Gem::Version
|
91
|
-
version:
|
77
|
+
version: 2.1.0
|
92
78
|
type: :development
|
93
79
|
prerelease: false
|
94
80
|
version_requirements: !ruby/object:Gem::Requirement
|
95
81
|
requirements:
|
96
|
-
- - "
|
82
|
+
- - "~>"
|
97
83
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
84
|
+
version: 2.1.0
|
99
85
|
- !ruby/object:Gem::Dependency
|
100
86
|
name: pry
|
101
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,13 +116,17 @@ files:
|
|
130
116
|
- bin/console
|
131
117
|
- bin/setup
|
132
118
|
- lib/pipekit.rb
|
133
|
-
- lib/pipekit/
|
119
|
+
- lib/pipekit/config.rb
|
134
120
|
- lib/pipekit/deal.rb
|
121
|
+
- lib/pipekit/deal_field.rb
|
122
|
+
- lib/pipekit/field_repository.rb
|
135
123
|
- lib/pipekit/note.rb
|
136
124
|
- lib/pipekit/person.rb
|
137
125
|
- lib/pipekit/person_field.rb
|
138
126
|
- lib/pipekit/repository.rb
|
139
127
|
- lib/pipekit/request.rb
|
128
|
+
- lib/pipekit/response.rb
|
129
|
+
- lib/pipekit/result.rb
|
140
130
|
- lib/pipekit/version.rb
|
141
131
|
- pipekit.gemspec
|
142
132
|
homepage: https://github.com/makersacademy/pipekit
|
@@ -152,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
152
142
|
requirements:
|
153
143
|
- - ">="
|
154
144
|
- !ruby/object:Gem::Version
|
155
|
-
version:
|
145
|
+
version: 1.9.1
|
156
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
147
|
requirements:
|
158
148
|
- - ">="
|
data/lib/pipekit/configurable.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require "active_support/inflector"
|
3
|
-
require "active_support/core_ext/hash/indifferent_access"
|
4
|
-
|
5
|
-
module Configurable
|
6
|
-
def config
|
7
|
-
self.class.config
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.included(base)
|
11
|
-
base.send :extend, ClassMethods
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods
|
15
|
-
def config_file_path
|
16
|
-
"./config/#{name.underscore}.yml"
|
17
|
-
end
|
18
|
-
|
19
|
-
def config
|
20
|
-
@config ||= load_config.with_indifferent_access
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def load_config
|
26
|
-
config_hash = load_from_yaml
|
27
|
-
|
28
|
-
config_hash.default_proc = proc do |hash, key|
|
29
|
-
raise Configurable::KeyNotFoundError.new(key)
|
30
|
-
end
|
31
|
-
|
32
|
-
config_hash
|
33
|
-
end
|
34
|
-
|
35
|
-
def load_from_yaml
|
36
|
-
yaml = ERB.new(File.read(config_file_path)).result
|
37
|
-
YAML.load(yaml)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class KeyNotFoundError < StandardError
|
42
|
-
def initialize(key)
|
43
|
-
super("The key #{key} was not found in the configuration file")
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|