insightly 0.2.8 → 0.2.9
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.
- data/README.md +5 -0
- data/lib/insightly/base.rb +31 -7
- data/lib/insightly/comment.rb +2 -2
- data/lib/insightly/link.rb +1 -1
- data/lib/insightly/link_helper.rb +38 -11
- data/lib/insightly/opportunity.rb +0 -5
- data/lib/insightly/organisation.rb +1 -1
- data/lib/insightly/task.rb +0 -7
- data/lib/insightly/task_link.rb +2 -0
- data/lib/insightly/task_link_helper.rb +83 -10
- data/lib/insightly/version.rb +1 -1
- data/spec/spec_helper.rb +54 -0
- data/spec/unit/comment_spec.rb +47 -12
- data/spec/unit/configuration_spec.rb +4 -3
- data/spec/unit/contact_info_spec.rb +1 -1
- data/spec/unit/contact_spec.rb +61 -48
- data/spec/unit/custom_field_spec.rb +2 -1
- data/spec/unit/link_spec.rb +1 -1
- data/spec/unit/opportunity_category_spec.rb +1 -0
- data/spec/unit/opportunity_spec.rb +23 -2
- data/spec/unit/opportunity_state_reason_spec.rb +2 -1
- data/spec/unit/organisation_spec.rb +32 -3
- data/spec/unit/relationship_spec.rb +1 -0
- data/spec/unit/tag_spec.rb +5 -5
- data/spec/unit/task_category_spec.rb +2 -1
- data/spec/unit/task_link_spec.rb +1 -0
- data/spec/unit/task_spec.rb +668 -197
- data/spec/unit/team_member_spec.rb +1 -0
- data/spec/unit/user_spec.rb +3 -2
- metadata +141 -149
data/README.md
CHANGED
@@ -6,6 +6,11 @@ This is a rest client library to handle talking to http://Insight.ly
|
|
6
6
|
The official API for Insigh.ly was released on August 12, 2012. This library is in the very early stages of implementing access to everything expose in
|
7
7
|
that API. The focus is primarily on opportunities and tasks.
|
8
8
|
|
9
|
+
Freedom isn't Free - Neither is the API
|
10
|
+
===========
|
11
|
+
|
12
|
+
You have to be a paying customer of Insight.ly to use the API. If you are on a free plan, then it will give you 401 errors when you try to do anything with the API
|
13
|
+
|
9
14
|
Getting Started
|
10
15
|
=========
|
11
16
|
|
data/lib/insightly/base.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
+
#METODO need to be able to delete records
|
2
|
+
#METODO create a script that can talk to the remote insightly install and confirm that everything still workd
|
3
|
+
#METODO re-write all the tests so that they store local data to work against
|
1
4
|
#METODO contacts allow you to set special dates to remember - can we access that via api?
|
2
|
-
#METODO It should have a method for converting date/time to the Insightly format
|
3
5
|
#METODO custom fields expect text,drop down, or date - add better handling (text should cast to_s)
|
4
6
|
#METODO contact infos should auto filter out duplicate entries
|
5
7
|
#METODO all the helpers should auto filter out duplicates (links,contact info,address etc)
|
6
8
|
#METODO only allow build to set fields that are part of the API fields
|
7
9
|
#METODO Fix all tests so they don't require the existence of certain object ids
|
8
|
-
|
10
|
+
#METODO add support for $top $skip and Brief=true for the things that suppor them
|
9
11
|
#METODO make a distinction between fields that you can set and save and ones you can only read - like DATE_UPDATED_UTC
|
12
|
+
|
10
13
|
module Insightly
|
11
14
|
class Base
|
12
15
|
|
13
16
|
class << self
|
14
|
-
attr_accessor :api_fields
|
17
|
+
attr_accessor :api_fields, :url_base, :remote_id_field
|
15
18
|
end
|
16
19
|
self.api_fields = []
|
17
20
|
|
@@ -46,6 +49,9 @@ module Insightly
|
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
52
|
+
def self.date_to_insightly(date = Date.today)
|
53
|
+
date.strftime("%Y-%m-%d %H:%M:%S")
|
54
|
+
end
|
49
55
|
def initialize(id = nil)
|
50
56
|
@data = {}
|
51
57
|
load(id) if id
|
@@ -55,8 +61,22 @@ module Insightly
|
|
55
61
|
def url_base
|
56
62
|
self.class.url_base
|
57
63
|
end
|
64
|
+
|
65
|
+
def remote_id_field
|
66
|
+
return self.class.remote_id_field if self.class.remote_id_field
|
67
|
+
self.class.to_s.downcase.gsub("insightly::", "") + "_id"
|
68
|
+
end
|
69
|
+
|
58
70
|
def remote_id
|
59
|
-
|
71
|
+
self.send(remote_id_field.to_sym)
|
72
|
+
end
|
73
|
+
|
74
|
+
def remote_id=(value)
|
75
|
+
self.send("#{remote_id_field}=", value)
|
76
|
+
end
|
77
|
+
|
78
|
+
def remote_id?
|
79
|
+
self.remote_id.nil? || self.remote_id.to_s.empty? ? false : true
|
60
80
|
end
|
61
81
|
|
62
82
|
def load(id)
|
@@ -67,9 +87,11 @@ module Insightly
|
|
67
87
|
def reload
|
68
88
|
load(remote_id)
|
69
89
|
end
|
90
|
+
|
70
91
|
def to_json
|
71
|
-
|
72
|
-
|
92
|
+
@data.to_json
|
93
|
+
end
|
94
|
+
|
73
95
|
def build(data)
|
74
96
|
@data = data
|
75
97
|
self
|
@@ -123,7 +145,9 @@ module Insightly
|
|
123
145
|
end
|
124
146
|
list
|
125
147
|
end
|
126
|
-
|
148
|
+
#def self.to_insightly_date(date)
|
149
|
+
# date.strftime("%Y-%m-%d 00:00:00")
|
150
|
+
#end
|
127
151
|
|
128
152
|
end
|
129
153
|
end
|
data/lib/insightly/comment.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#METODO Add support for FileAttachment xml
|
2
|
+
#METODO the load via xml and load via json do not return the same format data
|
2
3
|
module Insightly
|
3
4
|
class Comment < ReadWrite
|
4
|
-
|
5
|
+
self.url_base = "Comments"
|
5
6
|
|
6
7
|
api_field "COMMENT_ID",
|
7
8
|
"BODY",
|
@@ -41,7 +42,6 @@ module Insightly
|
|
41
42
|
|
42
43
|
def load(id)
|
43
44
|
load_from_xml(get_collection("#{url_base}/#{id}", :xml_raw))
|
44
|
-
|
45
45
|
end
|
46
46
|
|
47
47
|
def save
|
data/lib/insightly/link.rb
CHANGED
@@ -1,16 +1,43 @@
|
|
1
|
+
#METODO be able to ask the object for a list of project_ids and projects
|
1
2
|
module Insightly
|
2
3
|
module LinkHelper
|
3
4
|
def links
|
4
5
|
@data["LINKS"] ||= []
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
@data["LINKS"].collect { |a| Insightly::Link.build(a) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def links=(list)
|
10
|
+
@data["LINKS"] = list ? list.collect { |a| fix_for_link(a).remote_data } : []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_link(link)
|
14
|
+
@data["LINKS"] ||= []
|
15
|
+
@data["LINKS"] << fix_for_link(link).remote_data
|
16
|
+
true
|
17
|
+
end
|
18
|
+
def contact_ids
|
19
|
+
self.links.collect { |link| link.contact_id}.compact
|
20
|
+
end
|
21
|
+
def opportunity_ids
|
22
|
+
self.links.collect { |link| link.opportunity_id}.compact
|
23
|
+
end
|
24
|
+
def organisation_ids
|
25
|
+
self.links.collect { |link| link.organisation_id}.compact
|
26
|
+
end
|
27
|
+
def contacts
|
28
|
+
self.contact_ids.collect { |id| Insightly::Contact.new(id)}
|
29
|
+
end
|
30
|
+
def opportunities
|
31
|
+
self.opportunity_ids.collect { |id| Insightly::Opportunity.new(id)}
|
32
|
+
end
|
33
|
+
def organisations
|
34
|
+
self.organisation_ids.collect { |id| Insightly::Organisation.new(id)}
|
35
|
+
end
|
36
|
+
#def project_ids
|
37
|
+
# self.links.collect { |link| link.project_id}.compact
|
38
|
+
#end
|
39
|
+
#def projects
|
40
|
+
# self.project_ids.collect { |id| Insightly::Project.new(id)}
|
41
|
+
#end
|
15
42
|
end
|
16
|
-
end
|
43
|
+
end
|
data/lib/insightly/task.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#METODO make it so you can just say task.add_contact(contact/contact_id) (for opportunity and organisation as well)
|
2
|
-
#METODO warn them about how you have to save a task before you can link it up
|
3
1
|
module Insightly
|
4
2
|
class Task < ReadWrite
|
5
3
|
include Insightly::TaskLinkHelper
|
@@ -61,12 +59,7 @@ module Insightly
|
|
61
59
|
end
|
62
60
|
|
63
61
|
|
64
|
-
def remote_id
|
65
|
-
task_id
|
66
|
-
end
|
67
|
-
|
68
62
|
def fix_for_link(link)
|
69
|
-
#This needs to auto set the org id on the item
|
70
63
|
link.task_id = self.remote_id
|
71
64
|
link
|
72
65
|
end
|
data/lib/insightly/task_link.rb
CHANGED
@@ -1,16 +1,89 @@
|
|
1
|
+
#METODO add support for project
|
1
2
|
module Insightly
|
2
3
|
module TaskLinkHelper
|
3
4
|
def task_links
|
4
5
|
@data["TASKLINKS"] ||= []
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
@data["TASKLINKS"].collect { |a| Insightly::TaskLink.build(a) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def task_links=(list)
|
10
|
+
@data["TASKLINKS"] = list ? list.collect { |a| fix_for_link(a).remote_data } : []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_task_link(link)
|
14
|
+
raise(ScriptError, "You must save the #{self.class} before adding a link.") if !remote_id
|
15
|
+
@data["TASKLINKS"] ||= []
|
16
|
+
@data["TASKLINKS"] << fix_for_link(link).remote_data
|
17
|
+
true
|
18
|
+
end
|
19
|
+
def contact_ids
|
20
|
+
list_ids_by_type("contact")
|
21
|
+
end
|
22
|
+
|
23
|
+
def contacts
|
24
|
+
list_objs_by_type("contact", Insightly::Contact)
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_contact_id(contact_id)
|
28
|
+
add_by_id("contact", contact_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_contact(contact)
|
32
|
+
add_by_obj("contact", contact)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def opportunity_ids
|
38
|
+
list_ids_by_type("opportunity")
|
39
|
+
end
|
40
|
+
|
41
|
+
def opportunities
|
42
|
+
list_objs_by_type("opportunity", Insightly::Opportunity)
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_opportunity_id(opportunity_id)
|
46
|
+
add_by_id("opportunity", opportunity_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_opportunity(opportunity)
|
50
|
+
add_by_obj("opportunity", opportunity)
|
51
|
+
end
|
52
|
+
def organisation_ids
|
53
|
+
list_ids_by_type("organisation")
|
54
|
+
end
|
55
|
+
|
56
|
+
def organisations
|
57
|
+
list_objs_by_type("organisation", Insightly::Organisation)
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_organisation_id(organisation_id)
|
61
|
+
add_by_id("organisation", organisation_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_organisation(organisation)
|
65
|
+
add_by_obj("organisation", organisation)
|
66
|
+
end
|
67
|
+
|
68
|
+
def list_ids_by_type(type)
|
69
|
+
self.task_links.collect { |task_link| task_link.send("#{type}_id".to_sym) }.compact
|
70
|
+
end
|
71
|
+
|
72
|
+
def list_objs_by_type(type,klass)
|
73
|
+
self.send("#{type}_ids".to_sym).collect { |id| klass.new(id) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_by_id(type, id)
|
77
|
+
return false if !id
|
78
|
+
self.save if !remote_id?
|
79
|
+
add_task_link(Insightly::TaskLink.send("add_#{type}".to_sym,id))
|
80
|
+
self.save
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_by_obj(type, obj)
|
84
|
+
obj.save if !obj.remote_id?
|
85
|
+
self.send("add_#{type}_id".to_sym, obj.remote_id)
|
86
|
+
end
|
87
|
+
protected :list_ids_by_type, :list_objs_by_type, :add_by_id, :add_by_obj
|
15
88
|
end
|
16
89
|
end
|
data/lib/insightly/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -7,12 +7,30 @@
|
|
7
7
|
require "rubygems"
|
8
8
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
|
9
9
|
|
10
|
+
|
11
|
+
|
10
12
|
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
11
13
|
Bundler.require(:default, :testing) if defined?(Bundler)
|
12
14
|
|
15
|
+
|
16
|
+
require 'vcr'
|
17
|
+
|
18
|
+
VCR.configure do |c|
|
19
|
+
c.cassette_library_dir = 'cassettes'
|
20
|
+
c.hook_into :webmock
|
21
|
+
c.default_cassette_options = {:record => :new_episodes} # :new_episodes, :none, :all
|
22
|
+
c.ignore_localhost = true
|
23
|
+
# c.allow_http_connections_when_no_cassette = true
|
24
|
+
c.filter_sensitive_data('<API_KEY>') { INSIGHTLY_API_KEY }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
13
29
|
require "insightly"
|
14
30
|
require File.expand_path(File.dirname(__FILE__) + "/../api_key")
|
15
31
|
RSpec.configure do |config|
|
32
|
+
config.extend VCR::RSpec::Macros
|
33
|
+
|
16
34
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
17
35
|
config.run_all_when_everything_filtered = true
|
18
36
|
config.filter_run :focus
|
@@ -23,3 +41,39 @@ RSpec.configure do |config|
|
|
23
41
|
# --seed 1234
|
24
42
|
config.order = 'random'
|
25
43
|
end
|
44
|
+
|
45
|
+
def simple_insightly_user
|
46
|
+
|
47
|
+
VCR.use_cassette('all insightly users', :allow_playback_repeats => true) do
|
48
|
+
return Insightly::User.all.first
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
def simple_insightly_task(title = nil)
|
54
|
+
VCR.use_cassette('simple task', :allow_playback_repeats => true) do
|
55
|
+
task = Insightly::Task.new.build({"STATUS" => "Completed",
|
56
|
+
"RESPONSIBLE_USER_ID" => simple_insightly_user.user_id,
|
57
|
+
"OWNER_USER_ID" => simple_insightly_user.user_id,
|
58
|
+
"TITLE" => title ? title : "000 Test Task #{Date.today}"
|
59
|
+
})
|
60
|
+
task.save
|
61
|
+
return task
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
def simple_insightly_organisation(title = nil)
|
66
|
+
VCR.use_cassette('simple contact') do
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
def simple_insightly_opportunity(title = nil)
|
71
|
+
VCR.use_cassette('simple contact') do
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def simple_insightly_contact(first_name = nil, last_name = nil)
|
76
|
+
VCR.use_cassette('simple contact') do
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/spec/unit/comment_spec.rb
CHANGED
@@ -39,9 +39,25 @@ describe Insightly::Comment do
|
|
39
39
|
@comment.remote_id.should == @comment.comment_id
|
40
40
|
end
|
41
41
|
it "should allow you to load based on an id" do
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
VCR.use_cassette('create and load comment') do
|
43
|
+
@user = Insightly::User.all.first
|
44
|
+
|
45
|
+
@task = Insightly::Task.new
|
46
|
+
@task.title = "000 Test Task"
|
47
|
+
@task.status = "Completed"
|
48
|
+
@task.owner_user_id = @user.user_id
|
49
|
+
@task.responsible_user_id = @user.user_id
|
50
|
+
@task.save
|
51
|
+
@task.comment_on("Sample Comment")
|
52
|
+
@saved_comment = @task.comments.first
|
53
|
+
|
54
|
+
|
55
|
+
@comment = Insightly::Comment.new(@saved_comment.comment_id)
|
56
|
+
@comment.comment_id.should == @saved_comment.comment_id
|
57
|
+
@comment.body.should == @saved_comment.body
|
58
|
+
|
59
|
+
|
60
|
+
end
|
45
61
|
end
|
46
62
|
it "should allow you to build an object from a hash" do
|
47
63
|
comment = Insightly::Comment.new.build({"BODY" => "Other"})
|
@@ -77,15 +93,34 @@ describe Insightly::Comment do
|
|
77
93
|
end
|
78
94
|
|
79
95
|
it "should allow you to modify a comment" do
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
96
|
+
VCR.use_cassette('first insightly user') do
|
97
|
+
@user = Insightly::User.all.first
|
98
|
+
end
|
99
|
+
VCR.use_cassette('create and modify comment') do
|
100
|
+
|
101
|
+
|
102
|
+
@task = Insightly::Task.new
|
103
|
+
@task.title = "000 Test Task"
|
104
|
+
@task.status = "Completed"
|
105
|
+
@task.owner_user_id = @user.user_id
|
106
|
+
@task.responsible_user_id = @user.user_id
|
107
|
+
@task.save
|
108
|
+
@task.comment_on("Sample Comment")
|
109
|
+
@saved_comment = @task.comments.first
|
110
|
+
|
111
|
+
@comment = Insightly::Comment.new(@saved_comment.comment_id)
|
112
|
+
@comment.body.should == "Sample Comment"
|
113
|
+
|
114
|
+
value = "Test Comment Edit Now"
|
115
|
+
@comment.body = value
|
116
|
+
@comment.save
|
117
|
+
@comment.body.should == value
|
118
|
+
@comment.reload
|
119
|
+
@comment.body.should == value
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
89
124
|
end
|
90
125
|
|
91
126
|
|