timetree 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{.rubocom.yml → .rubocop.yml} +12 -9
- data/CHANGELOG.md +36 -10
- data/README.md +49 -18
- data/Rakefile +8 -6
- data/lib/timetree.rb +12 -7
- data/lib/timetree/api_error.rb +1 -1
- data/lib/timetree/base_client.rb +52 -0
- data/lib/timetree/calendar_app/access_token.rb +25 -0
- data/lib/timetree/calendar_app/client.rb +228 -0
- data/lib/timetree/configuration.rb +11 -5
- data/lib/timetree/http_command.rb +12 -14
- data/lib/timetree/models/activity.rb +14 -2
- data/lib/timetree/models/application.rb +15 -0
- data/lib/timetree/models/base_model.rb +37 -28
- data/lib/timetree/models/calendar.rb +40 -4
- data/lib/timetree/models/event.rb +35 -9
- data/lib/timetree/models/label.rb +2 -0
- data/lib/timetree/models/user.rb +2 -0
- data/lib/timetree/oauth_app/client.rb +256 -0
- data/lib/timetree/version.rb +1 -1
- data/timetree.gemspec +9 -10
- metadata +42 -52
- data/lib/timetree/client.rb +0 -298
@@ -5,15 +5,21 @@ require 'logger'
|
|
5
5
|
module TimeTree
|
6
6
|
# TimeTree apis client configuration.
|
7
7
|
class Configuration
|
8
|
-
# @return [String]
|
9
|
-
attr_accessor :
|
8
|
+
# @return [String] OAuthApp's access token
|
9
|
+
attr_accessor :oauth_app_token
|
10
|
+
|
11
|
+
# @return [String] CalendarApp's app id
|
12
|
+
attr_accessor :calendar_app_application_id
|
13
|
+
# @return [String] CalendarApp's private key content#
|
14
|
+
# e.g. File.read('<YOUR_PATH_TO_PEM_FILE>')
|
15
|
+
attr_accessor :calendar_app_private_key
|
16
|
+
|
10
17
|
# @return [Logger]
|
11
18
|
attr_accessor :logger
|
12
19
|
|
13
20
|
def initialize
|
14
|
-
logger = Logger.new
|
15
|
-
logger.level = :warn
|
16
|
-
@logger = logger
|
21
|
+
@logger = Logger.new $stdout
|
22
|
+
@logger.level = :warn
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -15,22 +15,22 @@ module TimeTree
|
|
15
15
|
# @param path [String] String or URI to access.
|
16
16
|
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
17
17
|
def get(path, params = {})
|
18
|
-
logger.info "GET #{connection.build_url("#{@host}#{path}", params)}"
|
18
|
+
@logger.info "GET #{connection.build_url("#{@host}#{path}", params)}"
|
19
19
|
res = connection.get path, params
|
20
20
|
@client.update_ratelimit(res)
|
21
|
-
logger.debug "Response status:#{res.status}, body:#{res.body}"
|
21
|
+
@logger.debug "Response status:#{res.status}, body:#{res.body}"
|
22
22
|
res
|
23
23
|
end
|
24
24
|
|
25
25
|
# @param path [String] String or URI to access.
|
26
26
|
# @param body_params [Hash]
|
27
27
|
# The request bodythat will eventually be converted to JSON.
|
28
|
-
def post(path, body_params = {})
|
28
|
+
def post(path, body_params = {}, &block)
|
29
29
|
@logger.debug "POST #{@host}#{path} body:#{body_params}"
|
30
|
-
headers = {
|
31
|
-
res = connection.run_request :post, path, body_params.to_json, headers
|
30
|
+
headers = {'Content-Type' => 'application/json'}
|
31
|
+
res = connection.run_request :post, path, body_params.to_json, headers, &block
|
32
32
|
@client.update_ratelimit(res)
|
33
|
-
logger.debug "Response status:#{res.status}, body:#{res.body}"
|
33
|
+
@logger.debug "Response status:#{res.status}, body:#{res.body}"
|
34
34
|
res
|
35
35
|
end
|
36
36
|
|
@@ -38,11 +38,11 @@ module TimeTree
|
|
38
38
|
# @param body_params [Hash]
|
39
39
|
# The request bodythat will eventually be converted to JSON.
|
40
40
|
def put(path, body_params = {})
|
41
|
-
logger.debug "PUT #{@host}#{path} body:#{body_params}"
|
42
|
-
headers = {
|
41
|
+
@logger.debug "PUT #{@host}#{path} body:#{body_params}"
|
42
|
+
headers = {'Content-Type' => 'application/json'}
|
43
43
|
res = connection.run_request :put, path, body_params.to_json, headers
|
44
44
|
@client.update_ratelimit(res)
|
45
|
-
logger.debug "Response status:#{res.status}, body:#{res.body}"
|
45
|
+
@logger.debug "Response status:#{res.status}, body:#{res.body}"
|
46
46
|
res
|
47
47
|
end
|
48
48
|
|
@@ -52,20 +52,18 @@ module TimeTree
|
|
52
52
|
@logger.debug "DELETE #{@host}#{path} params:#{params}"
|
53
53
|
res = connection.delete path, params
|
54
54
|
@client.update_ratelimit(res)
|
55
|
-
logger.debug "Response status:#{res.status}, body:#{res.body}"
|
55
|
+
@logger.debug "Response status:#{res.status}, body:#{res.body}"
|
56
56
|
res
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
attr_reader :logger
|
59
|
+
private
|
62
60
|
|
63
61
|
def connection
|
64
62
|
Faraday.new(
|
65
63
|
url: @host,
|
66
64
|
headers: base_request_headers
|
67
65
|
) do |builder|
|
68
|
-
builder.response :json, parser_options: {
|
66
|
+
builder.response :json, parser_options: {symbolize_names: true}, content_type: /\bjson$/
|
69
67
|
end
|
70
68
|
end
|
71
69
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'timetree/models/base_model'
|
4
|
+
|
3
5
|
module TimeTree
|
4
6
|
# Model for TimeTree comment.
|
5
7
|
class Activity < BaseModel
|
@@ -29,7 +31,7 @@ module TimeTree
|
|
29
31
|
# @since 0.0.1
|
30
32
|
def create
|
31
33
|
check_client
|
32
|
-
|
34
|
+
_create
|
33
35
|
end
|
34
36
|
|
35
37
|
#
|
@@ -39,8 +41,18 @@ module TimeTree
|
|
39
41
|
# @since 0.0.1
|
40
42
|
def data_params
|
41
43
|
{
|
42
|
-
data: {
|
44
|
+
data: {attributes: {content: content}}
|
43
45
|
}
|
44
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def _create
|
51
|
+
if @client.is_a?(CalendarApp::Client)
|
52
|
+
@client.create_activity(event_id, data_params)
|
53
|
+
else
|
54
|
+
@client.create_activity(calendar_id, event_id, data_params)
|
55
|
+
end
|
56
|
+
end
|
45
57
|
end
|
46
58
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'timetree/models/base_model'
|
4
|
+
|
5
|
+
module TimeTree
|
6
|
+
# Model for TimeTree application.
|
7
|
+
class Application < BaseModel
|
8
|
+
# @return [String]
|
9
|
+
attr_accessor :name
|
10
|
+
# @return [String]
|
11
|
+
attr_accessor :description
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :image_url
|
14
|
+
end
|
15
|
+
end
|
@@ -4,7 +4,7 @@ require 'time'
|
|
4
4
|
|
5
5
|
module TimeTree
|
6
6
|
# TimeTree base model object.
|
7
|
-
class BaseModel
|
7
|
+
class BaseModel # rubocop:disable Metrics/ClassLength
|
8
8
|
# @return [Array<Hash<String,String>>]
|
9
9
|
attr_accessor :relationships
|
10
10
|
# @return [String]
|
@@ -15,15 +15,15 @@ module TimeTree
|
|
15
15
|
# @param data [Hash]
|
16
16
|
# TimeTree apis's response data.
|
17
17
|
# @param included [Hash]
|
18
|
-
# @param client [TimeTree::Client]
|
19
|
-
# @return [TimeTree::User, TimeTree::Label, TimeTree::Calendar, TimeTree::Event, TimeTree::Activity]
|
18
|
+
# @param client [TimeTree::OAuthApp::Client, TimeTree::CalendarApp::Client]
|
19
|
+
# @return [TimeTree::User, TimeTree::Label, TimeTree::Calendar, TimeTree::Event, TimeTree::Activity, Hash]
|
20
20
|
# A TimeTree model object that be based on the type.
|
21
|
-
# @raise [TimeTree::Error] if the type property is not set
|
21
|
+
# @raise [TimeTree::Error] if the type property is not set.
|
22
22
|
# @since 0.0.1
|
23
|
-
def self.to_model(data, included: nil, client: nil)
|
23
|
+
def self.to_model(data, included: nil, client: nil) # rubocop:disable all
|
24
24
|
id = data[:id]
|
25
25
|
type = data[:type]
|
26
|
-
raise Error
|
26
|
+
raise Error.new('type is required.') if type.nil?
|
27
27
|
|
28
28
|
attributes = data[:attributes] || {}
|
29
29
|
relationships = data[:relationships] || {}
|
@@ -37,22 +37,26 @@ module TimeTree
|
|
37
37
|
}
|
38
38
|
|
39
39
|
case type
|
40
|
-
when '
|
41
|
-
|
42
|
-
when '
|
43
|
-
|
40
|
+
when 'activity'
|
41
|
+
Activity.new(**params)
|
42
|
+
when 'application'
|
43
|
+
Application.new(**params)
|
44
44
|
when 'calendar'
|
45
45
|
Calendar.new(**params)
|
46
46
|
when 'event'
|
47
47
|
Event.new(**params)
|
48
|
-
when '
|
49
|
-
|
48
|
+
when 'label'
|
49
|
+
Label.new(**params)
|
50
|
+
when 'user'
|
51
|
+
User.new(**params)
|
50
52
|
else
|
51
|
-
|
53
|
+
TimeTree.configuration.logger.warn("type '#{type}' is unknown. id:#{id}")
|
54
|
+
# when unexpected model type, return the 'data' argument.
|
55
|
+
data
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
55
|
-
def initialize(type:, id: nil, client: nil, attributes: nil, relationships: nil, included: nil)
|
59
|
+
def initialize(type:, id: nil, client: nil, attributes: nil, relationships: nil, included: nil) # rubocop:disable Metrics/ParameterLists
|
56
60
|
@type = type
|
57
61
|
@id = id
|
58
62
|
@client = client
|
@@ -64,33 +68,29 @@ module TimeTree
|
|
64
68
|
"\#<#{self.class}:#{object_id} id:#{id}>"
|
65
69
|
end
|
66
70
|
|
67
|
-
|
71
|
+
private
|
68
72
|
|
69
73
|
def check_client
|
70
|
-
raise Error
|
74
|
+
raise Error.new('@client is nil.') if @client.nil?
|
71
75
|
end
|
72
76
|
|
73
77
|
def to_model(data)
|
74
78
|
self.class.to_model data, client: @client
|
75
79
|
end
|
76
80
|
|
77
|
-
def set_attributes(attributes)
|
81
|
+
def set_attributes(attributes) # rubocop:disable Naming/AccessorMethodName
|
78
82
|
return unless attributes.is_a? Hash
|
79
83
|
return if attributes.empty?
|
80
84
|
|
81
|
-
setter_methods = self.class.instance_methods.select { |method| method.to_s.end_with? '=' }
|
82
85
|
attributes.each do |key, value|
|
83
|
-
|
84
|
-
next unless setter_methods.include? setter
|
86
|
+
next unless respond_to?("#{key}=".to_sym)
|
85
87
|
|
86
|
-
if defined?(self.class::TIME_FIELDS) && self.class::TIME_FIELDS.include?(key)
|
87
|
-
value = Time.parse value
|
88
|
-
end
|
88
|
+
value = Time.parse value if defined?(self.class::TIME_FIELDS) && self.class::TIME_FIELDS.include?(key)
|
89
89
|
instance_variable_set "@#{key}", value
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
def set_relationships(relationships, included)
|
93
|
+
def set_relationships(relationships, included) # rubocop:disable all
|
94
94
|
return unless relationships.is_a? Hash
|
95
95
|
return if relationships.empty?
|
96
96
|
return unless defined? self.class::RELATIONSHIPS
|
@@ -112,21 +112,30 @@ module TimeTree
|
|
112
112
|
set_relationship_data_if_included(included)
|
113
113
|
end
|
114
114
|
|
115
|
-
def set_relationship_data_if_included(included)
|
115
|
+
def set_relationship_data_if_included(included) # rubocop:disable all
|
116
116
|
@_relation_data_dic = {}
|
117
117
|
included.each do |data|
|
118
118
|
item = to_model(data)
|
119
119
|
next unless item
|
120
120
|
|
121
|
-
|
122
|
-
|
121
|
+
if item.is_a? Hash
|
122
|
+
item_id = item[:id]
|
123
|
+
item_type = item[:type]
|
124
|
+
else
|
125
|
+
item_id = item.id
|
126
|
+
item_type = item.type
|
127
|
+
end
|
128
|
+
next unless item_id && item_type
|
129
|
+
|
130
|
+
@_relation_data_dic[item_type] ||= {}
|
131
|
+
@_relation_data_dic[item_type][item_id] = item
|
123
132
|
end
|
124
133
|
detect_relation_data = lambda { |type, id|
|
125
134
|
return unless @_relation_data_dic[type]
|
126
135
|
|
127
136
|
@_relation_data_dic[type][id]
|
128
137
|
}
|
129
|
-
|
138
|
+
relationships.each do |key, id_data|
|
130
139
|
relation_data = nil
|
131
140
|
if id_data.is_a? Array
|
132
141
|
relation_data = []
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'timetree/models/base_model'
|
4
|
+
|
3
5
|
module TimeTree
|
4
6
|
# Model for TimeTree calendar.
|
5
7
|
class Calendar < BaseModel
|
@@ -30,7 +32,7 @@ module TimeTree
|
|
30
32
|
# @since 0.0.1
|
31
33
|
def event(event_id)
|
32
34
|
check_client
|
33
|
-
|
35
|
+
get_event(event_id)
|
34
36
|
end
|
35
37
|
|
36
38
|
#
|
@@ -46,7 +48,7 @@ module TimeTree
|
|
46
48
|
# @since 0.0.1
|
47
49
|
def upcoming_events(days: 7, timezone: 'UTC')
|
48
50
|
check_client
|
49
|
-
|
51
|
+
get_upcoming_event(days, timezone)
|
50
52
|
end
|
51
53
|
|
52
54
|
#
|
@@ -60,7 +62,7 @@ module TimeTree
|
|
60
62
|
return @members if defined? @members
|
61
63
|
|
62
64
|
check_client
|
63
|
-
@members =
|
65
|
+
@members = get_members
|
64
66
|
end
|
65
67
|
|
66
68
|
#
|
@@ -74,7 +76,41 @@ module TimeTree
|
|
74
76
|
return @labels if defined? @labels
|
75
77
|
|
76
78
|
check_client
|
77
|
-
@labels =
|
79
|
+
@labels = get_labels
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def get_event(event_id)
|
85
|
+
if @client.is_a?(CalendarApp::Client)
|
86
|
+
@client.event(event_id)
|
87
|
+
else
|
88
|
+
@client.event(id, event_id)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_upcoming_event(days, timezone)
|
93
|
+
if @client.is_a?(CalendarApp::Client)
|
94
|
+
@client.upcoming_events(days: days, timezone: timezone)
|
95
|
+
else
|
96
|
+
@client.upcoming_events(id, days: days, timezone: timezone)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_members
|
101
|
+
if @client.is_a?(CalendarApp::Client)
|
102
|
+
@client.calendar_members
|
103
|
+
else
|
104
|
+
@client.calendar_members(id)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_labels
|
109
|
+
if @client.is_a?(CalendarApp::Client)
|
110
|
+
raise Error.new 'CalendarApp does not support label api'
|
111
|
+
else
|
112
|
+
@client.calendar_labels(id)
|
113
|
+
end
|
78
114
|
end
|
79
115
|
end
|
80
116
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'timetree/models/base_model'
|
4
|
+
|
3
5
|
module TimeTree
|
4
6
|
# Model for TimeTree event or keep.
|
5
7
|
class Event < BaseModel
|
@@ -54,7 +56,7 @@ module TimeTree
|
|
54
56
|
# @since 0.0.1
|
55
57
|
def create
|
56
58
|
check_client
|
57
|
-
|
59
|
+
_create_event
|
58
60
|
end
|
59
61
|
|
60
62
|
#
|
@@ -66,7 +68,7 @@ module TimeTree
|
|
66
68
|
# @since 0.0.1
|
67
69
|
def update
|
68
70
|
check_client
|
69
|
-
|
71
|
+
_update_event
|
70
72
|
end
|
71
73
|
|
72
74
|
#
|
@@ -78,7 +80,7 @@ module TimeTree
|
|
78
80
|
# @since 0.0.1
|
79
81
|
def delete
|
80
82
|
check_client
|
81
|
-
|
83
|
+
_delete_event
|
82
84
|
end
|
83
85
|
|
84
86
|
#
|
@@ -90,7 +92,7 @@ module TimeTree
|
|
90
92
|
# @since 0.0.1
|
91
93
|
def create_comment(message)
|
92
94
|
check_client
|
93
|
-
params = {
|
95
|
+
params = {type: 'activity', attributes: {calendar_id: calendar_id, event_id: id, content: message}}
|
94
96
|
activity = to_model params
|
95
97
|
activity.create
|
96
98
|
end
|
@@ -120,15 +122,39 @@ module TimeTree
|
|
120
122
|
}
|
121
123
|
end
|
122
124
|
|
123
|
-
|
125
|
+
private
|
124
126
|
|
125
127
|
def relationships_params
|
126
|
-
current_label = label ? {
|
127
|
-
current_attendees = attendees ? attendees.map { |u| {
|
128
|
+
current_label = label ? {type: 'label', id: label.id} : relationships[:label]
|
129
|
+
current_attendees = attendees ? attendees.map { |u| {type: 'user', id: u.id} } : relationships[:attendees]
|
128
130
|
{
|
129
|
-
label: {
|
130
|
-
attendees: {
|
131
|
+
label: {data: current_label},
|
132
|
+
attendees: {data: current_attendees}
|
131
133
|
}
|
132
134
|
end
|
135
|
+
|
136
|
+
def _create_event
|
137
|
+
if @client.is_a?(CalendarApp::Client)
|
138
|
+
@client.create_event(data_params)
|
139
|
+
else
|
140
|
+
@client.create_event(calendar_id, data_params)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def _update_event
|
145
|
+
if @client.is_a?(CalendarApp::Client)
|
146
|
+
@client.update_event(id, data_params)
|
147
|
+
else
|
148
|
+
@client.update_event(calendar_id, id, data_params)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def _delete_event
|
153
|
+
if @client.is_a?(CalendarApp::Client)
|
154
|
+
@client.delete_event(id)
|
155
|
+
else
|
156
|
+
@client.delete_event(calendar_id, id)
|
157
|
+
end
|
158
|
+
end
|
133
159
|
end
|
134
160
|
end
|