timetree 0.0.1 → 0.1.4

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.
@@ -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