close 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9aa1bc5d8124785414823311b95736c9ddde2ca4c5f2746e6308443f313ace7a
4
- data.tar.gz: e3eac1f1c53b1da521845eb66bd3a8ec6cd342c608cf216b2cef62e7b0cea410
3
+ metadata.gz: 2854258a74b425cbfcde648007a00465eebb1c42b41ea3dcb5dd8b9af558839e
4
+ data.tar.gz: e1032fe4a32b4927b50d1aedf0bbfb6eb85b8af6e81f20a04fa77141f77f9ace
5
5
  SHA512:
6
- metadata.gz: 0a5b9a3e0ee10bced5cccad33bc838c8b156fad65c821f332f548a94d0a6eff47847a340b946b5e91b12c0503bc36c0af883347cca12aa95887ad4d62e3d3144
7
- data.tar.gz: 991f361451aedaf253e9d602f3f8975deb3495202fb2f294c058b32b4ba83ecd20a9d262e7ab2d23aff1ba0d64dea314cfe9c0c05c8773beac094dfdcc218e64
6
+ metadata.gz: 5506272793581e06aee3d9ca3f7302f512436978aaab7e9826dc8c68cedd2d91165a24dcff21ce549af48d05151e68b83930aea5e93679c7142ef7d37c788186
7
+ data.tar.gz: 815914f77867cbe2152eb143ffe4ace620b297ed0a0283512d324c55ae2520564138c26043ccb2e229cae735980801e61864de7664ceccf3d704f34dcad7ff8f
data/Gemfile CHANGED
@@ -12,3 +12,7 @@ gem "rspec", "~> 3.0"
12
12
  gem "rubocop", "~> 1.21"
13
13
 
14
14
  gem "faraday"
15
+
16
+ gem "webmock"
17
+
18
+ gem "sinatra"
data/Gemfile.lock CHANGED
@@ -8,17 +8,28 @@ PATH
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
+ addressable (2.8.1)
12
+ public_suffix (>= 2.0.2, < 6.0)
11
13
  ast (2.4.2)
14
+ crack (0.4.5)
15
+ rexml
12
16
  diff-lcs (1.5.0)
13
17
  faraday (2.6.0)
14
18
  faraday-net_http (>= 2.0, < 3.1)
15
19
  ruby2_keywords (>= 0.0.4)
16
20
  faraday-net_http (3.0.1)
21
+ hashdiff (1.0.1)
17
22
  json (2.6.2)
23
+ mustermann (3.0.0)
24
+ ruby2_keywords (~> 0.0.1)
18
25
  ostruct (0.5.5)
19
26
  parallel (1.22.1)
20
27
  parser (3.1.2.1)
21
28
  ast (~> 2.4.1)
29
+ public_suffix (5.0.0)
30
+ rack (2.2.4)
31
+ rack-protection (3.0.2)
32
+ rack
22
33
  rainbow (3.1.1)
23
34
  rake (13.0.6)
24
35
  regexp_parser (2.6.0)
@@ -50,7 +61,17 @@ GEM
50
61
  parser (>= 3.1.1.0)
51
62
  ruby-progressbar (1.11.0)
52
63
  ruby2_keywords (0.0.5)
64
+ sinatra (3.0.2)
65
+ mustermann (~> 3.0)
66
+ rack (~> 2.2, >= 2.2.4)
67
+ rack-protection (= 3.0.2)
68
+ tilt (~> 2.0)
69
+ tilt (2.0.11)
53
70
  unicode-display_width (2.3.0)
71
+ webmock (3.18.1)
72
+ addressable (>= 2.8.0)
73
+ crack (>= 0.3.2)
74
+ hashdiff (>= 0.4.0, < 2.0.0)
54
75
 
55
76
  PLATFORMS
56
77
  arm64-darwin-21
@@ -61,6 +82,8 @@ DEPENDENCIES
61
82
  rake (~> 13.0)
62
83
  rspec (~> 3.0)
63
84
  rubocop (~> 1.21)
85
+ sinatra
86
+ webmock
64
87
 
65
88
  BUNDLED WITH
66
89
  2.3.7
data/README.md CHANGED
@@ -70,6 +70,23 @@ lead.save
70
70
 
71
71
  ## Supported Resources
72
72
 
73
+ ### Advanced Filters
74
+ [Close API Docs](https://developer.close.com/resources/leads/)
75
+
76
+ Advanced Filters are a clever way to open up search to the API, with the caveat that they are very dense and appear to be written in search DSL (Elastic, Solr, etc).
77
+
78
+ I have tried to encapsulate the complexity of the DSL into a simple interface that is easy to use. This is mostly by defining queries before
79
+ they are run and then running them with parameters when they are needed.
80
+
81
+ An example of an advanced filter query:
82
+
83
+ ```ruby
84
+ # Run a prebuilt query
85
+ Close::AdvancedFilter.run('find_leads_by_email', {email: 'buster.bluth@gmail.com'})
86
+
87
+
88
+ ```
89
+
73
90
  ### Leads
74
91
  [Close API Docs](https://developer.close.com/resources/leads/)
75
92
 
data/close-0.1.0.gem ADDED
Binary file
@@ -0,0 +1,60 @@
1
+ {
2
+ "limit": null,
3
+ "query": {
4
+ "negate": false,
5
+ "queries": [
6
+ {
7
+ "negate": false,
8
+ "object_type": "lead",
9
+ "type": "object_type"
10
+ },
11
+ {
12
+ "negate": false,
13
+ "queries": [
14
+ {
15
+ "negate": false,
16
+ "related_object_type": "contact",
17
+ "related_query": {
18
+ "negate": false,
19
+ "queries": [
20
+ {
21
+ "negate": false,
22
+ "related_object_type": "contact_email",
23
+ "related_query": {
24
+ "negate": false,
25
+ "queries": [
26
+ {
27
+ "condition": {
28
+ "mode": "full_words",
29
+ "type": "text",
30
+ "value": "%EMAIL%"
31
+ },
32
+ "field": {
33
+ "field_name": "email",
34
+ "object_type": "contact_email",
35
+ "type": "regular_field"
36
+ },
37
+ "negate": false,
38
+ "type": "field_condition"
39
+ }
40
+ ],
41
+ "type": "and"
42
+ },
43
+ "this_object_type": "contact",
44
+ "type": "has_related"
45
+ }
46
+ ],
47
+ "type": "and"
48
+ },
49
+ "this_object_type": "lead",
50
+ "type": "has_related"
51
+ }
52
+ ],
53
+ "type": "and"
54
+ }
55
+ ],
56
+ "type": "and"
57
+ },
58
+ "results_limit": null,
59
+ "sort": []
60
+ }
data/lib/close/errors.rb CHANGED
@@ -3,4 +3,6 @@ module Close
3
3
  class InvalidRequestError < StandardError; end
4
4
  class AuthenticationError < StandardError; end
5
5
  class APIError < StandardError; end
6
+ class QueryNotFoundError < StandardError; end
7
+ class MissingParameterError < StandardError; end
6
8
  end
@@ -0,0 +1,99 @@
1
+ # This class attempts to abstract away the Advanced Filter API.
2
+ # It is very powerful and fast, but building the queries is very tedious.
3
+ # It allows to store preset queries as JSON for common queries that
4
+ # can be commited to the repo and validated. It also lets you
5
+ # define queries on the fly which can then be reused across the
6
+ # codebase without having to copy and paste the query all the time.
7
+ module Close
8
+ class Filter
9
+ extend APIOperations
10
+
11
+ @@queries = {}
12
+
13
+ # Executes a raw query against the Close API.
14
+ # @param [Hash] query The query to execute.
15
+ # @return [Array] An array of results.
16
+ def self.execute(query = {})
17
+ response = request(:post, 'api/v1/data/search/', query)
18
+ response['data']
19
+ end
20
+
21
+ # Executes a query by name.
22
+ # @param [String] name The name of the query to execute.
23
+ # @param [Hash] params The parameters to pass to the query.
24
+ # @return [Array] An array of results.
25
+ def self.run(name, params = {})
26
+ query_string = load_query_from_file(name)
27
+ expected_params = find_params(query_string)
28
+ preflight_params(params, expected_params)
29
+ parameterized_query = apply_params(query_string, params)
30
+ execute(parameterized_query)
31
+ end
32
+
33
+ # Loads a query from a file or from memory.
34
+ # @param [String] name The name of the query.
35
+ # @return [String] A stringified JSON query.
36
+ def self.load_query(name)
37
+ if @@queries[name.to_s]
38
+ @@queries[name.to_s]
39
+ else
40
+ load_query_from_file(name)
41
+ end
42
+ end
43
+
44
+ # This method is used to defined a query at run time.
45
+ # If a name collision occurs, the query will be overwritten.
46
+ # @param [String] name The name of the query.
47
+ # @param [Hash] query_body A hash with placeholders in keys.
48
+ # @return [Void]
49
+ def self.add_query(name, query_body)
50
+ @@queries[name.to_s] = query_body.to_json
51
+ end
52
+
53
+ # Applies the params to the query string.
54
+ # @param [String] query_string The stringified JSON query.
55
+ # @param [Hash] params The parameters to apply.
56
+ # @return [String] The stringified JSON query with the parameters applied.
57
+ def self.apply_params(query_string, params)
58
+ qs = query_string.dup
59
+ params.each do |key, value|
60
+ qs.gsub!(/%#{key.upcase}%/, value)
61
+ end
62
+ qs
63
+ end
64
+
65
+ # Check that all of the params are present in expected_params.
66
+ # @param [Hash] params The parameters to check.
67
+ # @param [Array] expected_params The expected parameters.
68
+ # @return [Void]
69
+ # @raise [Close::MissingParameterError] if a parameter is missing.
70
+ def self.preflight_params(params, expected_params)
71
+ expected_params.each do |param|
72
+ if !params.transform_keys(&:to_s).has_key?(param.to_s)
73
+ raise Close::MissingParameterError, "Missing parameter: #{param}"
74
+ end
75
+ end
76
+ end
77
+
78
+ # Loads a predefined query from a file.
79
+ # @param [String] name The name of the query.
80
+ # @return [String] A stringified JSON query.
81
+ # @raise [Close::QueryNotFoundError] if the file does not exist.
82
+ def self.load_query_from_file(name)
83
+ begin
84
+ file = File.read("lib/close/data/filters/#{name}.json")
85
+ rescue Errno::ENOENT
86
+ raise Close::QueryNotFoundError.new("Query #{name} not found.")
87
+ end
88
+ end
89
+
90
+ # Scans a string a returns the parameters it will expect
91
+ # when executed.
92
+ # @param [String] str The string to scan.
93
+ # @return [Array] An array of parameters.
94
+ def self.find_params(str)
95
+ str.scan(/%[A-Z]+(?:_[A-Z]+)*%/).map{ |x| x[1..-2].downcase }.uniq
96
+ end
97
+
98
+ end
99
+ end
data/lib/close/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Close
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/close.rb CHANGED
@@ -5,6 +5,7 @@ require "faraday"
5
5
  require_relative "close/close_object"
6
6
  require_relative "close/api_operations"
7
7
  require_relative "close/api_resource"
8
+ require_relative "close/filter"
8
9
  require_relative "close/resources"
9
10
  require_relative "close/errors"
10
11
  require_relative "close/version"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: close
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - JoyNerd LLC
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-18 00:00:00.000000000 Z
11
+ date: 2022-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ostruct
@@ -54,11 +54,14 @@ files:
54
54
  - LICENSE.txt
55
55
  - README.md
56
56
  - Rakefile
57
+ - close-0.1.0.gem
57
58
  - lib/close.rb
58
59
  - lib/close/api_operations.rb
59
60
  - lib/close/api_resource.rb
60
61
  - lib/close/close_object.rb
62
+ - lib/close/data/filters/find_lead_by_contact_email.json
61
63
  - lib/close/errors.rb
64
+ - lib/close/filter.rb
62
65
  - lib/close/resource/contact.rb
63
66
  - lib/close/resource/custom_activity_type.rb
64
67
  - lib/close/resource/lead.rb