mycrm 0.0.41

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c2d05640d72f98ddd72af6846858fbbd30e8acd6
4
+ data.tar.gz: fe35fee156ef2586a2badf552716e5ebff67c916
5
+ SHA512:
6
+ metadata.gz: e2dbbe09306e7f9455ed9b4516bab95407c6a2df10adb09a3938c426a3cb62edef108d2a828b14f6d8df2ede2aa2cd35699ce3bd392b1cf6dcf3099ff4844c1c
7
+ data.tar.gz: dd87fc2be3e1d11f458abf02c6026d76b2f621ee4a95bc0239067e2bd9be1bba6cc74aa48d38ac14392e6f17a8cb46629fb37870e58eb5f2ac6f4e261fed48ff
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Danilo Lo Santo
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,112 @@
1
+ # Mycrm Gem
2
+
3
+ A library to simplify communication with the MyCrm data service.
4
+
5
+ ## Configuration
6
+
7
+ ```
8
+ storage = Redis.new({"host"=>"localhost", "port"=>6379, "namespace"=>"loan-market-platform-development"})
9
+ Mycrm.configure do |conf|
10
+ conf.base_uri = 'https://api.endopoint.com'
11
+ conf.username = 'some-user-name'
12
+ conf.password = 'very-secret'
13
+ conf.logger = ::Logger.new(STDOUT)
14
+ conf.storage = storage
15
+ conf.token_storage_expiry = 600 # in seconds default 10 minutes (600)
16
+ # it overrides the default domains' values
17
+ conf.domains = {
18
+ sources: {
19
+ lm_contact_us: -9
20
+ },
21
+ purposes: {
22
+ first_home: 17207
23
+ },
24
+ loan_features:{
25
+ other: 16928
26
+ },
27
+ adviser_sources:{
28
+ loan_market_careers_website: 16
29
+ },
30
+ adviser_types:{
31
+ mortgage_broker: 7829
32
+ },
33
+ other_option:{
34
+ other: 36928
35
+ }
36
+ }
37
+ end
38
+ ```
39
+
40
+ ## Create a new simple lead
41
+
42
+ ```
43
+ lead = Mycrm::Facades::Lead.new
44
+ lead.first_name = 'Michael'
45
+ lead.last_name = 'Stephens'
46
+ lead.email = 'michael.stephens@example.com'
47
+ lead.mobile = '0555123457'
48
+ lead.home_address = '135 King Street, 2000, Sydney, NSW, Australia'
49
+ lead.mobile_validated = true
50
+ lead.allocated_broker_id = 858860
51
+ lead.source = 'Lm Contact Us'
52
+ lead.activity_title = 'Gem Enquiry!'
53
+ lead.activity_details = 'I would like to ask...'
54
+
55
+ lead.create!
56
+ ```
57
+
58
+ ## Create a new lead with loan application
59
+
60
+ ```
61
+ lead = Mycrm::Facades::LeadApplication.new
62
+ lead.first_name = 'Michael'
63
+ lead.last_name = 'Stephens'
64
+ lead.email = 'michael.stephens@example.com'
65
+ lead.mobile = '0555123457'
66
+ lead.home_address = '135 King Street, Sydney, 2000, NSW'
67
+ lead.mobile_validated = true
68
+ lead.allocated_broker_id = 858860
69
+ lead.source = 'Lm Contact Us'
70
+ lead.settlement_date = "2017-06-06T23:38:18.609Z"
71
+ lead.add_purpose 'First Home'
72
+ lead.add_feature 'Other'
73
+ lead.loan_term = 120
74
+ lead.loan_amount = 35000
75
+ lead.activity_title = 'Gem Enquiry!'
76
+ lead.activity_details = 'I would like to ask...'
77
+
78
+ lead.create!
79
+ ```
80
+
81
+ ## Create a new prospect adviser
82
+ ```
83
+ adviser = Mycrm::Models::Adviser.new
84
+ adviser.first_name = 'James'
85
+ adviser.last_name = 'Smith'
86
+ adviser.email = 'james.smith@example.com'
87
+ adviser.mobile = '0555123457'
88
+ adviser.business_phone = '0405666555'
89
+ adviser.business_address = '135 King Street, Sydney, 2000, NSW'
90
+ adviser.summary = "I'm looking for job!"
91
+ adviser.source = 'Loan Market Careers Website'
92
+ adviser.reporting_country = 'Australia'
93
+ adviser.type = 'Mortgage Broker'
94
+
95
+ adviser.create!
96
+ ```
97
+
98
+ ## Development
99
+
100
+ ### Running tests in development
101
+
102
+ Some of the tests run against the test instance of the actual My CRM API.
103
+
104
+ To run these locally add a `.env.local` file with `API_BASE_URI`, `API_USERNAME`, and `API_PASSWORD`:
105
+
106
+ ```
107
+ API_BASE_URI=https://testapi.nzfsg.co.nz
108
+ API_USERNAME=$api-user-email
109
+ API_PASSWORD=$api-user-password
110
+ ```
111
+
112
+ Get the propper details off one of the Loan Market developers.
@@ -0,0 +1,16 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ # require 'rdoc/task'
8
+
9
+ # RDoc::Task.new(:rdoc) do |rdoc|
10
+ # rdoc.rdoc_dir = 'rdoc'
11
+ # rdoc.title = 'Mycrm'
12
+ # rdoc.options << '--line-numbers'
13
+ # rdoc.rdoc_files.include('lib/**/*.rb')
14
+ # end
15
+
16
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+ require 'mycrm/core_ext'
3
+
4
+ require 'mycrm/attributes'
5
+ require 'mycrm/connectable'
6
+ require 'mycrm/domains'
7
+ require 'mycrm/facades'
8
+ require 'mycrm/models'
9
+ require 'mycrm/version'
10
+
11
+ module Mycrm
12
+ class << self
13
+ attr_reader :configuration
14
+
15
+ private
16
+
17
+ attr_writer :configuration
18
+
19
+ def set_domains
20
+ configuration.domains = YAML.load_file(domains_file)
21
+ .depp_merge(configuration.domains.to_h.stringify_keys_deep!)
22
+ .stringify_keys_deep!
23
+ end
24
+
25
+ def domains_file
26
+ File.join(File.dirname(__FILE__), 'mycrm/resources', 'domains.yml')
27
+ end
28
+ end
29
+
30
+ def self.configure
31
+ self.configuration = Configuration.new
32
+ yield(configuration)
33
+ set_domains
34
+ end
35
+
36
+ def self.log(level, message)
37
+ return unless configuration.logger
38
+ configuration.logger.send level, message
39
+ end
40
+
41
+ class Configuration
42
+ attr_accessor :base_uri, :username, :password, :logger, :domains, :storage, :token_storage_expiry
43
+ end
44
+
45
+ class ApiError < StandardError; end
46
+ class ResponseError < StandardError; end
47
+ class ConnectionError < StandardError; end
48
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'attributes/name'
2
+ require_relative 'attributes/date'
3
+ require_relative 'attributes/address'
4
+ require_relative 'attributes/deposit'
5
+ require_relative 'attributes/organization'
@@ -0,0 +1,13 @@
1
+ require 'virtus'
2
+
3
+ module Mycrm
4
+ module Attributes
5
+ class Address
6
+ include Virtus.model
7
+
8
+ attribute :home, String
9
+ attribute :mail, String
10
+ attribute :business, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'virtus'
2
+
3
+ module Mycrm
4
+ module Attributes
5
+ class Date
6
+ include Virtus.model
7
+
8
+ attribute :day, String
9
+ attribute :month, String
10
+ attribute :year, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ require 'virtus'
2
+
3
+ module Mycrm
4
+ module Attributes
5
+ class Deposit
6
+ include Virtus.model
7
+
8
+ attribute :amount, Integer
9
+ attribute :source_id, Integer
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require 'virtus'
2
+
3
+ module Mycrm
4
+ module Attributes
5
+ class Name
6
+ include Virtus.model
7
+
8
+ attribute :display, String #, default: :default_display_name
9
+ attribute :first, String
10
+ attribute :last, String
11
+ attribute :middle, String
12
+
13
+ def set_default_display_name
14
+ self.display = [first, last].join(' ') if first || last
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'virtus'
2
+
3
+ module Mycrm
4
+ module Attributes
5
+ class Organization
6
+ include Virtus.model
7
+
8
+ attribute :id, Integer
9
+ attribute :name, String
10
+ attribute :website, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,107 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Mycrm
5
+ # this module adds connectivity methods to the descendant's class
6
+ module Connectable
7
+ TOKEN_KEY = 'mycrm_auth_token_key'
8
+ DEFAULT_TOKEN_EXPIRE_IN_STORAGE = 600 # 10 minutes
9
+
10
+ def self.extended(descendant)
11
+ descendant.send(:extend, ExtendedMethods)
12
+ end
13
+
14
+ class << self
15
+ #create the connection with basic configurations
16
+ def connection
17
+ Faraday.new(url: Mycrm.configuration.base_uri) do |faraday|
18
+ faraday.request :url_encoded
19
+ if Mycrm.configuration.logger
20
+ faraday.response :logger, Mycrm.configuration.logger, bodies: true
21
+ end
22
+ yield(faraday) if block_given?
23
+ faraday.adapter Faraday.default_adapter
24
+ end
25
+ rescue Errno::ECONNREFUSED => e
26
+ raise ConnectionError, e
27
+ end
28
+
29
+ def token
30
+ @token || fetch_token
31
+ end
32
+
33
+ def fetch_token
34
+ storage = Mycrm.configuration.storage # Redis or any strategy that implement set and get
35
+
36
+ if storage
37
+ token = storage.get(TOKEN_KEY)
38
+ if token && !(token.nil? || token.empty?)
39
+ token
40
+ else
41
+ token_storage_expiry = Mycrm.configuration.token_storage_expiry || DEFAULT_TOKEN_EXPIRE_IN_STORAGE
42
+ token = fetch_token_from_mycrm
43
+ storage.set(TOKEN_KEY, token, ex: token_storage_expiry)
44
+ token
45
+ end
46
+ else
47
+ fetch_token_from_mycrm
48
+ end
49
+ end
50
+
51
+ def fetch_token_from_mycrm
52
+ connection.post('/Login', credentials).body
53
+ end
54
+
55
+ def credentials
56
+ %w(username password).each_with_object({}) do |f, o|
57
+ o[f] = Mycrm.configuration.send(f)
58
+ end
59
+ end
60
+
61
+ def session
62
+ @token = token
63
+ yield if block_given?
64
+ ensure
65
+ @token = nil
66
+ end
67
+ end
68
+
69
+ # private module to be extended
70
+ module ExtendedMethods
71
+
72
+ def connection
73
+ Mycrm::Connectable.connection do |faraday|
74
+ faraday.headers['Authorization'] = "Bearer #{skim(Mycrm::Connectable.token)}"
75
+ end
76
+ end
77
+
78
+ def respond(response)
79
+ raise ResponseError, skim(response.body) unless response.success?
80
+ raise ResponseError, 'Not found' if 'null'.eql?(skim(response.body))
81
+ parse(response.body)
82
+ end
83
+
84
+ def parse(body)
85
+ JSON.parse(body)
86
+ rescue JSON::ParserError
87
+ raise ResponseError, "#{skim(body)} is not in a valid format"
88
+ end
89
+
90
+ def skim(string)
91
+ string.to_s.gsub(/\A"|"\Z/, '')
92
+ end
93
+
94
+ %w(get post put delete).each do |method|
95
+ define_method method do |uri, body = {}, query = {}|
96
+ response = connection.send(method, uri, query) do |req|
97
+ req.headers['Content-Type'] = 'application/json'
98
+ req.body = JSON.generate(body)
99
+ req.options.timeout = 120
100
+ req.options.open_timeout = 20
101
+ end
102
+ respond(response)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,7 @@
1
+ require 'mycrm/core_ext/string'
2
+ require 'mycrm/core_ext/hash'
3
+
4
+ String.include Mycrm::CoreExt::String
5
+ Symbol.include Mycrm::CoreExt::String
6
+
7
+ Hash.include Mycrm::CoreExt::Hash
@@ -0,0 +1,26 @@
1
+ module Mycrm
2
+ module CoreExt
3
+ class Address
4
+ FIELDS = [:formatted, :street, :suburb, :state, :postcode, :country]
5
+
6
+ attr_reader *FIELDS
7
+
8
+ alias to_s formatted
9
+
10
+ def initialize(formatted, locale = :en_au)
11
+ @formatted = formatted
12
+ expand(locale)
13
+ end
14
+
15
+ def to_h
16
+ FIELDS.reject{ |f| f == :formatted }.each_with_object({}){ |f, out| out[f] = send(f) }
17
+ end
18
+
19
+ private
20
+
21
+ def expand(locale)
22
+ @street, @suburb, @state, @postcode, @country = *formatted.split(',').map(&:strip)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module Mycrm
2
+ module CoreExt
3
+ module Hash
4
+ def symbolize_keys_deep!
5
+ self.replace modify_keys_deep(:to_sym)
6
+ end
7
+
8
+ def stringify_keys_deep!
9
+ self.replace modify_keys_deep(:to_s)
10
+ end
11
+
12
+ def modify_keys_deep(method = :to_sym )
13
+ self.each_with_object({}) do |(key, value), copy|
14
+ copy[key.send(method)] = value.kind_of?(Hash) ? value.modify_keys_deep(method) : value
15
+ end
16
+ end
17
+
18
+ def depp_merge(newhash)
19
+ return self.dup unless newhash
20
+ # merger = -> (key, oldval, newval) { oldval.is_a? Hash ? oldval.recursive_merge(newval) : newval } # not working :(
21
+ new_hash = (newhash.keys - keys).each_with_object({}) { |k, nh| nh[k] = newhash[k] }
22
+ self.each_with_object(new_hash) do |(key, value), nh|
23
+ nh[key] = value.kind_of?(Hash) ? value.depp_merge(newhash[key]) : (newhash[key] || value)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end