timetree 0.0.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,99 +1,148 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'time'
4
- require 'faraday'
5
- require 'faraday_middleware'
6
-
7
3
  module TimeTree
4
+ # TimeTree apis client.
8
5
  class Client
9
6
  API_HOST = 'https://timetreeapis.com'
10
-
7
+ # @return [String]
8
+ attr_reader :token
9
+ # @return [Integer]
11
10
  attr_reader :ratelimit_limit
11
+ # @return [Integer]
12
12
  attr_reader :ratelimit_remaining
13
+ # @return [Time]
13
14
  attr_reader :ratelimit_reset_at
14
15
 
15
- def initialize(access_token = nil)
16
- config = TimeTree.configuration
17
- @access_token = access_token || config.access_token
18
- @logger = config.logger
19
- end
20
-
21
- def inspect
22
- limit_info = nil
23
- if @ratelimit_limit
24
- limit_info = " ratelimit:#{@ratelimit_remaining}/#{@ratelimit_limit}"
25
- end
26
- if @ratelimit_reset_at
27
- limit_info = "#{limit_info}, reset_at:#{@ratelimit_reset_at.strftime('%m/%d %R')}"
28
- end
29
- "\#<#{self.class}:#{object_id}#{limit_info}>"
16
+ # @param token [String] a TimeTree's access token.
17
+ def initialize(token = nil)
18
+ @token = token || TimeTree.configuration.token
19
+ check_token
20
+ @http_cmd = HttpCommand.new(API_HOST, self)
30
21
  end
31
22
 
32
23
  #
33
- # User
24
+ # Get current user information.
34
25
  #
35
-
36
- def user
37
- res = get '/user'
38
- raise if res.status != 200
26
+ # @return [TimeTree::User]
27
+ # @raise [TimeTree::ApiError] if the http response status will not success.
28
+ # @since 0.0.1
29
+ def current_user
30
+ res = @http_cmd.get '/user'
31
+ raise ApiError, res if res.status != 200
39
32
 
40
33
  to_model res.body[:data]
41
34
  end
42
35
 
43
36
  #
44
- # Calendar
37
+ # Get a single calendar's information.
45
38
  #
46
-
39
+ # @param cal_id [String] calendar's id.
40
+ # @param include_relationships [Array<symbol>]
41
+ # includes association's object in the response.
42
+ # @return [TimeTree::Calendar]
43
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
44
+ # @raise [TimeTree::ApiError] if the http response status will not success.
45
+ # @since 0.0.1
47
46
  def calendar(cal_id, include_relationships: nil)
47
+ check_calendar_id cal_id
48
48
  params = relationships_params(include_relationships, Calendar::RELATIONSHIPS)
49
- res = get "/calendars/#{cal_id}", params
50
- raise if res.status != 200
49
+ res = @http_cmd.get "/calendars/#{cal_id}", params
50
+ raise ApiError, res if res.status != 200
51
51
 
52
52
  to_model(res.body[:data], included: res.body[:included])
53
53
  end
54
54
 
55
+ #
56
+ # Get calendar list that current user can access.
57
+ #
58
+ # @param include_relationships [Array<symbol>]
59
+ # includes association's object in the response.
60
+ # @return [Array<TimeTree::Calendar>]
61
+ # @raise [TimeTree::ApiError] if the http response status will not success.
62
+ # @since 0.0.1
55
63
  def calendars(include_relationships: nil)
56
64
  params = relationships_params(include_relationships, Calendar::RELATIONSHIPS)
57
- res = get '/calendars', params
58
- raise if res.status != 200
65
+ res = @http_cmd.get '/calendars', params
66
+ raise ApiError, res if res.status != 200
59
67
 
60
68
  included = res.body[:included]
61
69
  res.body[:data].map { |item| to_model(item, included: included) }
62
70
  end
63
71
 
72
+ #
73
+ # Get a calendar's label information used in event.
74
+ #
75
+ # @param cal_id [String] calendar's id.
76
+ # @return [Array<TimeTree::Label>]
77
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
78
+ # @raise [TimeTree::ApiError] if the http response status will not success.
79
+ # @since 0.0.1
64
80
  def calendar_labels(cal_id)
65
- res = get "/calendars/#{cal_id}/labels"
66
- raise if res.status != 200
81
+ check_calendar_id cal_id
82
+ res = @http_cmd.get "/calendars/#{cal_id}/labels"
83
+ raise ApiError, res if res.status != 200
67
84
 
68
85
  res.body[:data].map { |item| to_model(item) }
69
86
  end
70
87
 
88
+ #
89
+ # Get a calendar's member information.
90
+ #
91
+ # @param cal_id [String] calendar's id.
92
+ # @return [Array<TimeTree::User>]
93
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
94
+ # @raise [TimeTree::ApiError] if the http response status will not success.
95
+ # @since 0.0.1
71
96
  def calendar_members(cal_id)
72
- res = get "/calendars/#{cal_id}/members"
73
- raise if res.status != 200
97
+ check_calendar_id cal_id
98
+ res = @http_cmd.get "/calendars/#{cal_id}/members"
99
+ raise ApiError, res if res.status != 200
74
100
 
75
101
  res.body[:data].map { |item| to_model item }
76
102
  end
77
103
 
78
104
  #
79
- # Schedule/Keep
105
+ # Get the event's information.
80
106
  #
81
-
107
+ # @param cal_id [String] calendar's id.
108
+ # @param event_id [String] event's id.
109
+ # @param include_relationships [Array<symbol>]
110
+ # includes association's object in the response.
111
+ # @return [TimeTree::Event]
112
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
113
+ # @raise [TimeTree::Error] if the event_id arg is empty.
114
+ # @raise [TimeTree::ApiError] if the http response status will not success.
115
+ # @since 0.0.1
82
116
  def event(cal_id, event_id, include_relationships: nil)
117
+ check_calendar_id cal_id
118
+ check_event_id event_id
83
119
  params = relationships_params(include_relationships, Event::RELATIONSHIPS)
84
- res = get "/calendars/#{cal_id}/events/#{event_id}", params
85
- raise if res.status != 200
120
+ res = @http_cmd.get "/calendars/#{cal_id}/events/#{event_id}", params
121
+ raise ApiError, res if res.status != 200
86
122
 
87
123
  ev = to_model(res.body[:data], included: res.body[:included])
88
124
  ev.calendar_id = cal_id
89
125
  ev
90
126
  end
91
127
 
128
+ #
129
+ # Get the events' information after a request date.
130
+ #
131
+ # @param cal_id[String] calendar's id.
132
+ # @param days [Integer] The number of days to get.
133
+ # @param timezone [String] Timezone.
134
+ # @param include_relationships [Array<symbol>]
135
+ # includes association's object in the response.
136
+ # @return [Array<TimeTree::Event>]
137
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
138
+ # @raise [TimeTree::ApiError] if the http response status will not success.
139
+ # @since 0.0.1
92
140
  def upcoming_events(cal_id, days: 7, timezone: 'UTC', include_relationships: nil)
141
+ check_calendar_id cal_id
93
142
  params = relationships_params(include_relationships, Event::RELATIONSHIPS)
94
143
  params.merge!(days: days, timezone: timezone)
95
- res = get "/calendars/#{cal_id}/upcoming_events", params
96
- raise if res.status != 200
144
+ res = @http_cmd.get "/calendars/#{cal_id}/upcoming_events", params
145
+ raise ApiError, res if res.status != 200
97
146
 
98
147
  included = res.body[:included]
99
148
  res.body[:data].map do |item|
@@ -103,38 +152,84 @@ module TimeTree
103
152
  end
104
153
  end
105
154
 
155
+ #
156
+ # Creates an event to the calendar.
157
+ #
158
+ # @param cal_id [String] calendar's id.
159
+ # @param params [Hash] TimeTree request body format.
160
+ # @return [TimeTree::Event]
161
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
162
+ # @raise [TimeTree::ApiError] if the http response status will not success.
163
+ # @since 0.0.1
106
164
  def create_event(cal_id, params)
107
- res = post "/calendars/#{cal_id}/events", params
108
- raise if res.status != 201
165
+ check_calendar_id cal_id
166
+ res = @http_cmd.post "/calendars/#{cal_id}/events", params
167
+ raise ApiError, res if res.status != 201
109
168
 
110
169
  ev = to_model res.body[:data]
111
170
  ev.calendar_id = cal_id
112
171
  ev
113
172
  end
114
173
 
174
+ #
175
+ # Updates an event.
176
+ #
177
+ # @param cal_id [String] calendar's id.
178
+ # @param event_id [String] event's id.
179
+ # @param params [Hash]
180
+ # event's information specified in TimeTree request body format.
181
+ # @return [TimeTree::Event]
182
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
183
+ # @raise [TimeTree::Error] if the event_id arg is empty.
184
+ # @raise [TimeTree::ApiError] if the http response status will not success.
185
+ # @since 0.0.1
115
186
  def update_event(cal_id, event_id, params)
116
- res = put "/calendars/#{cal_id}/events/#{event_id}", params
117
- raise if res.status != 200
187
+ check_calendar_id cal_id
188
+ check_event_id event_id
189
+ res = @http_cmd.put "/calendars/#{cal_id}/events/#{event_id}", params
190
+ raise ApiError, res if res.status != 200
118
191
 
119
192
  ev = to_model res.body[:data]
120
193
  ev.calendar_id = cal_id
121
194
  ev
122
195
  end
123
196
 
197
+ #
198
+ # Deletes an event.
199
+ #
200
+ # @param cal_id [String] calendar's id.
201
+ # @param event_id [String] event's id.
202
+ # @return [true] if the operation succeeded.
203
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
204
+ # @raise [TimeTree::Error] if the event_id arg is empty.
205
+ # @raise [TimeTree::ApiError] if the http response status will not success.
206
+ # @since 0.0.1
124
207
  def delete_event(cal_id, event_id)
125
- res = delete "/calendars/#{cal_id}/events/#{event_id}"
126
- raise if res.status != 204
208
+ check_calendar_id cal_id
209
+ check_event_id event_id
210
+ res = @http_cmd.delete "/calendars/#{cal_id}/events/#{event_id}"
211
+ raise ApiError, res if res.status != 204
127
212
 
128
213
  true
129
214
  end
130
215
 
131
216
  #
132
- # Activity
217
+ # Creates comment to an event.
133
218
  #
134
-
219
+ # @param cal_id [String] calendar's id.
220
+ # @param event_id [String] event's id.
221
+ # @param params [Hash]
222
+ # comment's information specified in TimeTree request body format.
223
+ # @return [TimeTree::Activity]
224
+ # @raise [TimeTree::Error] if the cal_id arg is empty.
225
+ # @raise [TimeTree::Error] if the event_id arg is empty.
226
+ # @raise [TimeTree::ApiError] if the http response status is not success.
227
+ # @since 0.0.1
135
228
  def create_activity(cal_id, event_id, params)
136
- res = post "/calendars/#{cal_id}/events/#{event_id}/activities", params
137
- raise if res.status != 201
229
+ check_calendar_id cal_id
230
+ check_event_id event_id
231
+ res = @http_cmd.post "/calendars/#{cal_id}/events/#{event_id}/activities", params
232
+ raise ApiError, res if res.status != 201
138
233
 
139
234
  activity = to_model res.body[:data]
140
235
  activity.calendar_id = cal_id
@@ -142,21 +237,22 @@ module TimeTree
142
237
  activity
143
238
  end
144
239
 
145
- private
146
-
147
- attr_reader :logger
148
-
149
- def to_model(data, included: nil)
150
- TimeTree::BaseModel.to_model data, client: self, included: included
151
- end
152
-
153
- def relationships_params(relationships, default)
154
- params = {}
155
- relationships ||= default
156
- params[:include] = relationships.join ',' if relationships.is_a? Array
157
- params
240
+ def inspect
241
+ limit_info = nil
242
+ if defined?(@ratelimit_limit) && @ratelimit_limit
243
+ limit_info = " ratelimit:#{@ratelimit_remaining}/#{@ratelimit_limit}"
244
+ end
245
+ if defined?(@ratelimit_reset_at) && @ratelimit_reset_at
246
+ limit_info = "#{limit_info}, reset_at:#{@ratelimit_reset_at.strftime('%m/%d %R')}"
247
+ end
248
+ "\#<#{self.class}:#{object_id}#{limit_info}>"
158
249
  end
159
250
 
251
+ #
252
+ # update ratelimit properties
253
+ #
254
+ # @param res [Faraday::Response]
255
+ # apis http response.
160
256
  def update_ratelimit(res)
161
257
  limit = res.headers['x-ratelimit-limit']
162
258
  remaining = res.headers['x-ratelimit-remaining']
@@ -166,54 +262,37 @@ module TimeTree
166
262
  @ratelimit_reset_at = Time.at reset.to_i if reset
167
263
  end
168
264
 
169
- def get(path, params = {})
170
- logger.info "GET #{connection.build_url("#{API_HOST}#{path}", params)}"
171
- res = connection.get path, params
172
- update_ratelimit(res)
173
- logger.debug "Response status:#{res.status}, body:#{res.body}"
174
- res
265
+ private
266
+
267
+ def check_token
268
+ check_required_property(@token, 'token')
175
269
  end
176
270
 
177
- def put(path, params = {})
178
- logger.debug "PUT #{API_HOST}#{path} body:#{params}"
179
- res = connection.put path do |req|
180
- req.headers['Content-Type'] = 'application/json'
181
- req.body = params.to_json
182
- end
183
- update_ratelimit(res)
184
- logger.debug "Response status:#{res.status}, body:#{res.body}"
185
- res
271
+ def check_calendar_id(value)
272
+ check_required_property(value, 'calendar_id')
186
273
  end
187
274
 
188
- def post(path, params = {})
189
- @logger.debug "POST #{API_HOST}#{path} body:#{params}"
190
- res = connection.post path, params do |req|
191
- req.headers['Content-Type'] = 'application/json'
192
- req.body = params.to_json
193
- end
194
- update_ratelimit(res)
195
- logger.debug "Response status:#{res.status}, body:#{res.body}"
196
- res
197
- end
198
-
199
- def delete(path, params = {})
200
- @logger.debug "DELETE #{API_HOST}#{path} params:#{params}"
201
- res = connection.delete path, params
202
- update_ratelimit(res)
203
- logger.debug "Response status:#{res.status}, body:#{res.body}"
204
- res
205
- end
206
-
207
- def connection
208
- Faraday.new(
209
- url: API_HOST,
210
- headers: {
211
- 'Accept' => 'application/vnd.timetree.v1+json',
212
- 'Authorization' => "Bearer #{@access_token}"
213
- }
214
- ) do |builder|
215
- builder.response :json, parser_options: { symbolize_names: true }, content_type: /\bjson$/
216
- end
275
+ def check_event_id(value)
276
+ check_required_property(value, 'event_id')
277
+ end
278
+
279
+ def check_required_property(value, name)
280
+ err = Error.new "#{name} is required."
281
+ raise err if value.nil?
282
+ raise err if value.to_s.empty?
283
+
284
+ true
285
+ end
286
+
287
+ def to_model(data, included: nil)
288
+ TimeTree::BaseModel.to_model data, client: self, included: included
289
+ end
290
+
291
+ def relationships_params(relationships, default)
292
+ params = {}
293
+ relationships ||= default
294
+ params[:include] = relationships.join ',' if relationships.is_a? Array
295
+ params
217
296
  end
218
297
  end
219
298
  end
@@ -3,9 +3,13 @@
3
3
  require 'logger'
4
4
 
5
5
  module TimeTree
6
+ # TimeTree apis client configuration.
6
7
  class Configuration
7
- attr_accessor :access_token
8
+ # @return [String]
9
+ attr_accessor :token
10
+ # @return [Logger]
8
11
  attr_accessor :logger
12
+
9
13
  def initialize
10
14
  logger = Logger.new(STDOUT)
11
15
  logger.level = :warn
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TimeTree
4
+ # Model for TimeTree event or keep.
5
+ class Event < BaseModel
6
+ # @return [Striing]
7
+ attr_accessor :category
8
+ # @return [Striing]
9
+ attr_accessor :title
10
+ # @return [Boolean]
11
+ attr_accessor :all_day
12
+ # @return [Time]
13
+ attr_accessor :start_at
14
+ # @return [Striing]
15
+ attr_accessor :start_timezone
16
+ # @return [Time]
17
+ attr_accessor :end_at
18
+ # @return [String]
19
+ attr_accessor :end_timezone
20
+ # @return [Array<String>]
21
+ attr_accessor :recurrence
22
+ # @return [String]
23
+ attr_accessor :recurring_uuid
24
+ # @return [String]
25
+ attr_accessor :description
26
+ # @return [String]
27
+ attr_accessor :location
28
+ # @return [String]
29
+ attr_accessor :url
30
+ # @return [Time]
31
+ attr_accessor :updated_at
32
+ # @return [Time]
33
+ attr_accessor :created_at
34
+ # calendar's id.
35
+ # @return [String]
36
+ attr_accessor :calendar_id
37
+
38
+ # @return [TimeTree::User]
39
+ attr_reader :creator
40
+ # @return [TimeTree::Label]
41
+ attr_accessor :label
42
+ # @return [Array<TimeTree::User>]
43
+ attr_accessor :attendees
44
+
45
+ TIME_FIELDS = %i[start_at end_at updated_at created_at].freeze
46
+ RELATIONSHIPS = %i[creator label attendees].freeze
47
+
48
+ #
49
+ # Creates an event to the associated calendar.
50
+ #
51
+ # @return [TimeTree::Event]
52
+ # @raise [TimeTree::Error] if @client or @calendar_id is empty.
53
+ # @raise [TimeTree::ApiError] if the http response status will not success.
54
+ # @since 0.0.1
55
+ def create
56
+ check_client
57
+ @client.create_event calendar_id, data_params
58
+ end
59
+
60
+ #
61
+ # Updates the event.
62
+ #
63
+ # @return [TimeTree::Event]
64
+ # @raise [TimeTree::Error] if @client, @calendar_id or @id is empty.
65
+ # @raise [TimeTree::ApiError] if the http response status will not success.
66
+ # @since 0.0.1
67
+ def update
68
+ check_client
69
+ @client.update_event calendar_id, id, data_params
70
+ end
71
+
72
+ #
73
+ # Deletes the event.
74
+ #
75
+ # @return [true] if the operation succeeded.
76
+ # @raise [TimeTree::Error] if @client, @calendar_id or @id is empty.
77
+ # @raise [TimeTree::ApiError] if the http response status will not success.
78
+ # @since 0.0.1
79
+ def delete
80
+ check_client
81
+ @client.delete_event calendar_id, id
82
+ end
83
+
84
+ #
85
+ # Creates comment to the event.
86
+ #
87
+ # @return [TimeTree::Activity]
88
+ # @raise [TimeTree::Error] if @client, @calendar_id or @id is empty.
89
+ # @raise [TimeTree::ApiError] if the http response status will not success.
90
+ # @since 0.0.1
91
+ def create_comment(message)
92
+ check_client
93
+ params = { type: 'activity', attributes: { calendar_id: calendar_id, event_id: id, content: message } }
94
+ activity = to_model params
95
+ activity.create
96
+ end
97
+
98
+ #
99
+ # convert to a TimeTree request body format.
100
+ #
101
+ # @return [Hash]
102
+ # @since 0.0.1
103
+ def data_params
104
+ {
105
+ data: {
106
+ attributes: {
107
+ category: category,
108
+ title: title,
109
+ all_day: all_day,
110
+ start_at: start_at.iso8601,
111
+ start_timezone: start_timezone,
112
+ end_at: end_at.iso8601,
113
+ end_timezone: end_timezone,
114
+ description: description,
115
+ location: location,
116
+ url: url
117
+ },
118
+ relationships: relationships_params
119
+ }
120
+ }
121
+ end
122
+
123
+ private
124
+
125
+ def relationships_params
126
+ current_label = label ? { type: 'label', id: label.id } : @relationships[:label]
127
+ current_attendees = attendees ? attendees.map { |u| { type: 'user', id: u.id } } : @relationships[:attendees]
128
+ {
129
+ label: { data: current_label },
130
+ attendees: { data: current_attendees }
131
+ }
132
+ end
133
+ end
134
+ end