cronofy 0.0.5 → 0.37.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,119 +1,1975 @@
1
- module Cronofy
1
+ require 'uri'
2
2
 
3
+ module Cronofy
4
+ # Public: Primary class for interacting with the Cronofy API.
3
5
  class Client
6
+ include TimeEncoding
4
7
 
5
- def initialize(client_id, client_secret, token=nil, refresh_token=nil)
6
- @auth = Auth.new(client_id, client_secret, token, refresh_token)
8
+ # Public: The scope to request if none is explicitly specified by the
9
+ # caller.
10
+ DEFAULT_OAUTH_SCOPE = %w{
11
+ read_account
12
+ read_events
13
+ create_event
14
+ delete_event
15
+ }.freeze
16
+
17
+ # Public: Initialize a new Cronofy::Client.
18
+ #
19
+ # options - A Hash of options used to initialize the client (default: {}):
20
+ # :access_token - An existing access token String for the user's
21
+ # account (optional).
22
+ # :client_id - The client ID String of your Cronofy OAuth
23
+ # application (default:
24
+ # ENV["CRONOFY_CLIENT_ID"]).
25
+ # :client_secret - The client secret String of your Cronofy OAuth
26
+ # application (default:
27
+ # ENV["CRONOFY_CLIENT_SECRET"]).
28
+ # :refresh_token - An existing refresh token String for the user's
29
+ # account (optional).
30
+ # :data_center - An identifier to override the default data
31
+ # center (optional).
32
+ def initialize(options = {})
33
+ access_token = options[:access_token]
34
+ refresh_token = options[:refresh_token]
35
+
36
+ @client_id = options.fetch(:client_id, ENV["CRONOFY_CLIENT_ID"])
37
+ @client_secret = options.fetch(:client_secret, ENV["CRONOFY_CLIENT_SECRET"])
38
+ @data_center = options[:data_center] || options[:data_centre]
39
+
40
+ @auth = Auth.new(
41
+ client_id: @client_id,
42
+ client_secret: @client_secret,
43
+ access_token: access_token,
44
+ refresh_token: refresh_token,
45
+ data_center: @data_center
46
+ )
7
47
  end
8
48
 
9
- def access_token!
10
- raise CredentialsMissingError.new unless @auth.access_token
11
- @auth.access_token
49
+ # Public: Creates a new calendar for the profile.
50
+ #
51
+ # profile_id - The String ID of the profile to create the calendar within.
52
+ # name - The String to use as the name of the calendar.
53
+ # options - The Hash options used to customize the calendar
54
+ # (default: {}):
55
+ # :color - The color to make the calendar (optional).
56
+ #
57
+ # See https://www.cronofy.com/developers/api/#create-calendar for reference.
58
+ #
59
+ # Returns the created Calendar
60
+ #
61
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
62
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
63
+ # longer valid.
64
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
65
+ # include the required scope.
66
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
67
+ # parameters.
68
+ # Raises Cronofy::AccountLockedError if the profile is not in a writable
69
+ # state and so a calendar cannot be created.
70
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
71
+ # limits for the application.
72
+ def create_calendar(profile_id, name, options = {})
73
+ request = options.merge(profile_id: profile_id, name: name)
74
+ response = post("/v1/calendars", request)
75
+ parse_json(Calendar, "calendar", response)
12
76
  end
13
77
 
14
- # Public : Lists the calendars or the user across all of the calendar accounts
15
- # see http://www.cronofy.com/developers/api#calendars
78
+ # Public: Lists all the calendars for the account.
79
+ #
80
+ # See http://www.cronofy.com/developers/api#calendars for reference.
16
81
  #
17
- # Returns Hash of calendars
82
+ # Returns an Array of Calendars
83
+ #
84
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
85
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
86
+ # longer valid.
87
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
88
+ # limits for the application.
18
89
  def list_calendars
19
- response = do_request { access_token!.get("/v1/calendars") }
20
- ResponseParser.new(response).parse_json
90
+ response = get("/v1/calendars")
91
+ parse_collection(Calendar, "calendars", response)
21
92
  end
22
93
 
23
- # Public : Creates or updates an existing event that matches the event_id, in the calendar
24
- # see: http://www.cronofy.com/developers/api#upsert-event
25
- # aliased as upsert_event
94
+ # Public: Creates or updates an event for the event_id in the calendar
95
+ # relating to the given calendar_id.
26
96
  #
27
- # calendar_id - String Cronofy ID for the the calendar to contain the event
28
- # event - Hash describing the event with symbolized keys.
29
- # :event_id String client identifier for event NOT Cronofy's
30
- # :summary String
31
- # :start Time
32
- # :end Time
97
+ # calendar_id - The String Cronofy ID for the calendar to upsert the event
98
+ # to.
99
+ # event - A Hash describing the event with symbolized keys:
100
+ # :event_id - A String uniquely identifying the event for
101
+ # your application (note: this is NOT an ID
102
+ # generated by Cronofy).
103
+ # :summary - A String to use as the summary, sometimes
104
+ # referred to as the name or title, of the
105
+ # event.
106
+ # :description - A String to use as the description, sometimes
107
+ # referred to as the notes or body, of the
108
+ # event.
109
+ # :start - The Time or Date the event starts.
110
+ # :end - The Time or Date the event ends.
111
+ # :url - The URL associated with the event.
112
+ # :location - A Hash describing the location of the event
113
+ # with symbolized keys (optional):
114
+ # :description - A String describing the
115
+ # location.
116
+ # :lat - A String of the location's latitude.
117
+ # :long - A String of the location's longitude.
118
+ # :reminders - An Array of Hashes describing the desired
119
+ # reminders for the event. Reminders should be
120
+ # specified in priority order as, for example,
121
+ # when the underlying provider only supports a
122
+ # single reminder then the first reminder will
123
+ # be used.
124
+ # :minutes - An Integer specifying the number
125
+ # of minutes before the start of the
126
+ # event that the reminder should
127
+ # occur.
128
+ # :transparency - The transparency state for the event (optional).
129
+ # Accepted values are "transparent" and "opaque".
130
+ # :color - The color of the event (optional).
131
+ # :attendees - A Hash of :invite and :reject, each of which is
132
+ # an array of invitees to invite to or reject from
133
+ # the event. Invitees are represented by a hash of
134
+ # :email and :display_name (optional).
33
135
  #
34
- # Returns nothing
35
- def create_or_update_event(calendar_id, event)
136
+ # Examples
137
+ #
138
+ # client.upsert_event(
139
+ # "cal_n23kjnwrw2_jsdfjksn234",
140
+ # event_id: "qTtZdczOccgaPncGJaCiLg",
141
+ # summary: "Board meeting",
142
+ # description: "Discuss plans for the next quarter.",
143
+ # start: Time.utc(2014, 8, 5, 15, 30),
144
+ # end: Time.utc(2014, 8, 5, 17, 30),
145
+ # location: {
146
+ # description: "Board room",
147
+ # lat: "1.2345",
148
+ # long: "0.1234"
149
+ # })
150
+ #
151
+ # See http://www.cronofy.com/developers/api#upsert-event for reference.
152
+ #
153
+ # Returns nothing.
154
+ #
155
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
156
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
157
+ # longer valid.
158
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
159
+ # include the required scope.
160
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
161
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
162
+ # parameters.
163
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
164
+ # limits for the application.
165
+ def upsert_event(calendar_id, event)
36
166
  body = event.dup
37
- body[:start] = event[:start].utc.iso8601
38
- body[:end] = event[:end].utc.iso8601
39
167
 
40
- headers = {
41
- 'Content-Type' => 'application/json'
168
+ body[:start] = encode_event_time(body[:start])
169
+ body[:end] = encode_event_time(body[:end])
170
+
171
+ post("/v1/calendars/#{calendar_id}/events", body)
172
+ nil
173
+ end
174
+
175
+ # Public: Alias for #upsert_event
176
+ alias_method :create_or_update_event, :upsert_event
177
+
178
+ # Public: Returns a lazily-evaluated Enumerable of Events that satisfy the
179
+ # given query criteria.
180
+ #
181
+ # options - The Hash options used to refine the selection (default: {}):
182
+ # :from - The minimum Date from which to return events
183
+ # (optional).
184
+ # :to - The Date to return events up until (optional).
185
+ # :tzid - A String representing a known time zone
186
+ # identifier from the IANA Time Zone Database
187
+ # (default: Etc/UTC).
188
+ # :include_deleted - A Boolean specifying whether events that have
189
+ # been deleted should be included or excluded
190
+ # from the results (optional).
191
+ # :include_moved - A Boolean specifying whether events that have
192
+ # ever existed within the given window should
193
+ # be included or excluded from the results
194
+ # (optional).
195
+ # :include_managed - A Boolean specifying whether events that you
196
+ # are managing for the account should be
197
+ # included or excluded from the results
198
+ # (optional).
199
+ # :only_managed - A Boolean specifying whether only events that
200
+ # you are managing for the account should
201
+ # trigger notifications (optional).
202
+ # :localized_times - A Boolean specifying whether the start and
203
+ # end times should be returned with any
204
+ # available localization information
205
+ # (optional).
206
+ # :last_modified - The Time that events must be modified on or
207
+ # after in order to be returned (optional).
208
+ # :calendar_ids - An Array of calendar ids for restricting the
209
+ # returned events (optional).
210
+ # :include_geo - A Boolean specifying whether the events should
211
+ # have their location.lat and location.long
212
+ # returned where available (optional).
213
+ #
214
+ # The first page will be retrieved eagerly so that common errors will happen
215
+ # inline. However, subsequent pages (if any) will be requested lazily.
216
+ #
217
+ # See http://www.cronofy.com/developers/api#read-events for reference.
218
+ #
219
+ # Returns a lazily-evaluated Enumerable of Events
220
+ #
221
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
222
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
223
+ # longer valid.
224
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
225
+ # include the required scope.
226
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
227
+ # parameters.
228
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
229
+ # limits for the application.
230
+ def read_events(options = {})
231
+ params = READ_EVENTS_DEFAULT_PARAMS.merge(options)
232
+
233
+ READ_EVENTS_TIME_PARAMS.select { |tp| params.key?(tp) }.each do |tp|
234
+ params[tp] = to_iso8601(params[tp])
235
+ end
236
+
237
+ url = api_url + "/v1/events"
238
+ PagedResultIterator.new(PagedEventsResult, :events, access_token!, url, params)
239
+ end
240
+
241
+ # Public: Returns a lazily-evaluated Enumerable of FreeBusy that satisfy the
242
+ # given query criteria.
243
+ #
244
+ # options - The Hash options used to refine the selection (default: {}):
245
+ # :from - The minimum Date from which to return events
246
+ # (optional).
247
+ # :to - The Date to return events up until (optional).
248
+ # :tzid - A String representing a known time zone
249
+ # identifier from the IANA Time Zone Database
250
+ # (default: Etc/UTC).
251
+ # :include_managed - A Boolean specifying whether events that you
252
+ # are managing for the account should be
253
+ # included or excluded from the results
254
+ # (optional).
255
+ # :localized_times - A Boolean specifying whether the start and
256
+ # end times should be returned with any
257
+ # available localization information
258
+ # (optional).
259
+ # :calendar_ids - An Array of calendar ids for restricting the
260
+ # returned events (optional).
261
+ #
262
+ # The first page will be retrieved eagerly so that common errors will happen
263
+ # inline. However, subsequent pages (if any) will be requested lazily.
264
+ #
265
+ # See http://www.cronofy.com/developers/api/#free-busy for reference.
266
+ #
267
+ # Returns a lazily-evaluated Enumerable of FreeBusy
268
+ #
269
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
270
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
271
+ # longer valid.
272
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
273
+ # include the required scope.
274
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
275
+ # parameters.
276
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
277
+ # limits for the application.
278
+ def free_busy(options = {})
279
+ params = FREE_BUSY_DEFAULT_PARAMS.merge(options)
280
+
281
+ FREE_BUSY_TIME_PARAMS.select { |tp| params.key?(tp) }.each do |tp|
282
+ params[tp] = to_iso8601(params[tp])
283
+ end
284
+
285
+ url = api_url + "/v1/free_busy"
286
+ PagedResultIterator.new(PagedFreeBusyResult, :free_busy, access_token!, url, params)
287
+ end
288
+
289
+ # Public: Deletes an event from the specified calendar
290
+ #
291
+ # calendar_id - The String Cronofy ID for the calendar to delete the event
292
+ # from.
293
+ # event_id - A String uniquely identifying the event for your application
294
+ # (note: this is NOT an ID generated by Cronofy).
295
+ #
296
+ # See http://www.cronofy.com/developers/api#delete-event for reference.
297
+ #
298
+ # Returns nothing.
299
+ #
300
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
301
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
302
+ # longer valid.
303
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
304
+ # include the required scope.
305
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
306
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
307
+ # parameters.
308
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
309
+ # limits for the application.
310
+ def delete_event(calendar_id, event_id)
311
+ delete("/v1/calendars/#{calendar_id}/events", event_id: event_id)
312
+ nil
313
+ end
314
+
315
+ class BatchBuilder
316
+ include TimeEncoding
317
+
318
+ def initialize
319
+ @entries = []
320
+ end
321
+
322
+ def upsert_event(calendar_id, event)
323
+ data = event.dup
324
+
325
+ data[:start] = encode_event_time(data[:start])
326
+ data[:end] = encode_event_time(data[:end])
327
+
328
+ post "/v1/calendars/#{calendar_id}/events", data
329
+ end
330
+
331
+ alias_method :create_or_update_event, :upsert_event
332
+
333
+ def delete_event(calendar_id, event_id)
334
+ delete "/v1/calendars/#{calendar_id}/events", event_id: event_id
335
+ end
336
+
337
+ def delete_external_event(calendar_id, event_uid)
338
+ delete "/v1/calendars/#{calendar_id}/events", event_uid: event_uid
339
+ end
340
+
341
+ def add_entry(args)
342
+ @entries << BatchEntryRequest.new(args)
343
+ nil
344
+ end
345
+
346
+ def build
347
+ @entries.dup
348
+ end
349
+
350
+ private
351
+
352
+ def delete(relative_url, data)
353
+ add_entry(method: "DELETE", relative_url: relative_url, data: data)
354
+ end
355
+
356
+ def post(relative_url, data)
357
+ add_entry(method: "POST", relative_url: relative_url, data: data)
358
+ end
359
+ end
360
+
361
+ def batch
362
+ yield builder = BatchBuilder.new
363
+
364
+ requests = builder.build
365
+
366
+ response = post("/v1/batch", batch: requests)
367
+ responses = parse_collection(BatchEntryResponse, "batch", response)
368
+
369
+ entries = requests.zip(responses).map do |request, response|
370
+ response.request = request
371
+ response
372
+ end
373
+
374
+ result = BatchResponse.new(entries)
375
+
376
+ if result.errors?
377
+ msg = "Batch contains #{result.errors.count} errors"
378
+ raise BatchResponse::PartialSuccessError.new(msg, result)
379
+ end
380
+
381
+ result
382
+ end
383
+
384
+ # Public: Deletes an external event from the specified calendar
385
+ #
386
+ # calendar_id - The String Cronofy ID for the calendar to delete the event
387
+ # from.
388
+ # event_uid - The unique ID of the event to delete.
389
+ #
390
+ # Returns nothing.
391
+ #
392
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
393
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
394
+ # longer valid.
395
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
396
+ # include the required scope or the client has not been granted elevated
397
+ # permissions to the calendar.
398
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
399
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
400
+ # parameters.
401
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
402
+ # limits for the application.
403
+ def delete_external_event(calendar_id, event_uid)
404
+ delete("/v1/calendars/#{calendar_id}/events", event_uid: event_uid)
405
+ nil
406
+ end
407
+
408
+ # Public: Deletes all events you are managing for the account.
409
+ #
410
+ # See https://www.cronofy.com/developers/api/#bulk-delete-events for
411
+ # reference.
412
+ #
413
+ # options - The Hash options used to refine the selection (optional):
414
+ # :calendar_ids - An Array of calendar ids to delete managed
415
+ # events from returned events.
416
+ #
417
+ # If no options are specified it defaults to deleting all the events you are
418
+ # managing.
419
+ #
420
+ # Returns nothing.
421
+ #
422
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
423
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
424
+ # longer valid.
425
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
426
+ # include the required scope.
427
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
428
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
429
+ # parameters.
430
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
431
+ # limits for the application.
432
+ def delete_all_events(options = nil)
433
+ options ||= { delete_all: true }
434
+ delete("/v1/events", options)
435
+ nil
436
+ end
437
+
438
+ # Public: Creates a notification channel with a callback URL
439
+ #
440
+ # callback_url - A String specifing the callback URL for the channel.
441
+ # options - The Hash options used to refine the notifications of the
442
+ # channel (default: {}):
443
+ # :filters - A Hash of filters to use for the notification
444
+ # channel (optional):
445
+ # :calendar_ids - An Array of calendar ID strings
446
+ # to restrict the returned events
447
+ # to (optional).
448
+ # :only_managed - A Boolean specifying whether
449
+ # only events that you are
450
+ # managing for the account should
451
+ # trigger notifications
452
+ # (optional).
453
+ #
454
+ # See http://www.cronofy.com/developers/api#create-channel for reference.
455
+ #
456
+ # Returns a Channel.
457
+ #
458
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
459
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
460
+ # longer valid.
461
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
462
+ # include the required scope.
463
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
464
+ # parameters.
465
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
466
+ # limits for the application.
467
+ def create_channel(callback_url, options = {})
468
+ params = options.merge(callback_url: callback_url)
469
+
470
+ response = post("/v1/channels", params)
471
+ parse_json(Channel, "channel", response)
472
+ end
473
+
474
+ # DEPRECATED: Please use hmac_valid instead.
475
+ def hmac_match?(args)
476
+ warn "[DEPRECATION] `hmac_match?` is deprecated. Please use `hmac_valid?` instead."
477
+ hmac_valid?(args)
478
+ end
479
+
480
+ # Public: Verifies a HMAC from a push notification using the client secret.
481
+ #
482
+ # args - A Hash containing the details of the push notification:
483
+ # :body - A String of the body of the notification.
484
+ # :hmac - A String containing comma-separated values describing HMACs of the notification taken from the
485
+ # Cronofy-HMAC-SHA256 header.
486
+ #
487
+ # Returns true if one of the HMAC provided matches the one calculated using the
488
+ # client secret, otherwise false.
489
+ def hmac_valid?(args)
490
+ body = args[:body]
491
+ hmac = args[:hmac]
492
+
493
+ return false if hmac.nil? || hmac.empty?
494
+
495
+ sha256 = OpenSSL::Digest.new('sha256')
496
+ digest = OpenSSL::HMAC.digest(sha256, @client_secret, body)
497
+ calculated = Base64.encode64(digest).strip
498
+
499
+ hmac_list = hmac.split(',')
500
+ hmac_list.include?(calculated)
501
+ end
502
+
503
+ # Public: Lists all the notification channels for the account.
504
+ #
505
+ # See http://www.cronofy.com/developers/api#list-channels for reference.
506
+ #
507
+ # Returns an Array of Channels.
508
+ #
509
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
510
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
511
+ # longer valid.
512
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
513
+ # include the required scope.
514
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
515
+ # limits for the application.
516
+ def list_channels
517
+ response = get("/v1/channels")
518
+ parse_collection(Channel, "channels", response)
519
+ end
520
+
521
+ # Public: Closes a notification channel.
522
+ #
523
+ # channel_id - The String Cronofy ID for the channel to close.
524
+ #
525
+ # See http://www.cronofy.com/developers/api#close-channel for reference.
526
+ #
527
+ # Returns nothing.
528
+ #
529
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
530
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
531
+ # longer valid.
532
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
533
+ # include the required scope.
534
+ # Raises Cronofy::NotFoundError if the channel does not exist.
535
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
536
+ # limits for the application.
537
+ def close_channel(channel_id)
538
+ delete("/v1/channels/#{channel_id}")
539
+ nil
540
+ end
541
+
542
+ # Public: Retrieves the details of the account.
543
+ #
544
+ # See http://www.cronofy.com/developers/api#account for reference.
545
+ #
546
+ # Returns an Account.
547
+ #
548
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
549
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
550
+ # longer valid.
551
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
552
+ # include the required scope.
553
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
554
+ # limits for the application.
555
+ def account
556
+ response = get("/v1/account")
557
+ parse_json(Account, "account", response)
558
+ end
559
+
560
+ # Public: Lists all the profiles for the account.
561
+ #
562
+ # See https://www.cronofy.com/developers/api/#profiles for reference.
563
+ #
564
+ # Returns an Array of Profiles
565
+ #
566
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
567
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
568
+ # longer valid.
569
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
570
+ # limits for the application.
571
+ def list_profiles
572
+ response = get("/v1/profiles")
573
+ parse_collection(Profile, "profiles", response)
574
+ end
575
+
576
+ # Public: Retrieves the userinfo for the account
577
+ #
578
+ # See http://openid.net/specs/openid-connect-core-1_0.html#UserInfo for
579
+ # reference.
580
+ #
581
+ # Returns an UserInfo.
582
+ #
583
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
584
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
585
+ # longer valid.
586
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
587
+ # include the required scope.
588
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
589
+ # limits for the application.
590
+ def userinfo
591
+ response = get("/v1/userinfo")
592
+ parse_json(UserInfo, nil, response)
593
+ end
594
+
595
+
596
+ # Public: Changes the participation status for a calendar event
597
+ #
598
+ # calendar_id - The String Cronofy ID for the calendar to delete the event
599
+ # from.
600
+ # event_uid - A String uniquely identifying the event for your application
601
+ # (note: this is NOT an ID generated by Cronofy).
602
+ # status - A String or Symbol to set the participation status of the
603
+ # invite to
604
+ #
605
+ #
606
+ # See http://www.cronofy.com/developers/api#delete-event for reference.
607
+ #
608
+ # Returns nothing.
609
+ #
610
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
611
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
612
+ # longer valid.
613
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
614
+ # include the required scope.
615
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
616
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
617
+ # parameters.
618
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
619
+ # limits for the application.
620
+ def change_participation_status(calendar_id, event_uid, status)
621
+ body = {
622
+ status: status.to_s,
42
623
  }
43
624
 
44
- do_request { access_token!.post("/v1/calendars/#{calendar_id}/events", { body: JSON.generate(body), headers: headers }) }
625
+ url = "/v1/calendars/#{calendar_id}/events/#{event_uid}/participation_status"
626
+ post(url, body)
45
627
  end
46
- alias_method :upsert_event, :create_or_update_event
47
628
 
48
- # Public : Deletes an event from the specified calendar
49
- # see http://www.cronofy.com/developers/api#delete-event
629
+ # Public: Attempts to authorize the email with impersonation from a service
630
+ # account
50
631
  #
51
- # calendar_id - String Cronofy ID for the calendar containing the event
52
- # event_id - String client ID for the event
632
+ # email - the email address to impersonate
633
+ # scope - Array or String of scopes describing the access to
634
+ # request from the user to the users calendars (required).
635
+ # callback_url - the url to callback to
53
636
  #
54
637
  # Returns nothing
55
- def delete_event(calendar_id, event_id)
56
- params = { event_id: event_id }
638
+ #
639
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
640
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
641
+ # longer valid.
642
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
643
+ # limits for the application.
644
+ def authorize_with_service_account(email, scope, callback_url)
645
+ if scope.respond_to?(:join)
646
+ scope = scope.join(' ')
647
+ end
57
648
 
58
- do_request { access_token!.delete("/v1/calendars/#{calendar_id}/events", { params: params }) }
649
+ params = {
650
+ email: email,
651
+ scope: scope,
652
+ callback_url: callback_url
653
+ }
654
+ post("/v1/service_account_authorizations", params)
655
+ nil
59
656
  end
60
657
 
61
- # Public : Generate the authorization URL to send the user to in order to generate
62
- # and authorization code in order for an access_token to be issued
63
- # see http://www.cronofy.com/developers/api#authorization
658
+ # Public: Generates a URL to send the user to in order to perform the OAuth
659
+ # 2.0 authorization process.
64
660
  #
65
- # redirect_uri - String URI to return the user to once authorization process completed
66
- # scope - Array of scopes describing access required to the users calendars (default: all scopes)
661
+ # redirect_uri - A String specifing the URI to return the user to once they
662
+ # have completed the authorization steps.
663
+ # options - The Hash options used to refine the selection
664
+ # (default: {}):
665
+ # :scope - Array or String of scopes describing the access to
666
+ # request from the user to the users calendars
667
+ # (default: DEFAULT_OAUTH_SCOPE).
668
+ # :state - Array of states to retain during the OAuth
669
+ # authorization process (optional).
67
670
  #
68
- # Returns String
69
- def user_auth_link(redirect_uri, scope=nil)
70
- @auth.user_auth_link(redirect_uri, scope)
671
+ # See http://www.cronofy.com/developers/api#authorization for reference.
672
+ #
673
+ # Returns the URL as a String.
674
+ def user_auth_link(redirect_url, options = {})
675
+ options = { scope: DEFAULT_OAUTH_SCOPE }.merge(options)
676
+ @auth.user_auth_link(redirect_url, options)
71
677
  end
72
678
 
73
- # Public : Returns the access_token for a given code and redirect_uri pair
74
- # see http://www.cronofy.com/developers/api#token-issue
679
+ # Public: Retrieves the OAuth credentials authorized for the given code and
680
+ # redirect URL pair.
681
+ #
682
+ # code - String code returned to redirect_url after authorization.
683
+ # redirect_url - A String specifing the URL the user returned to once they
684
+ # had completed the authorization steps.
75
685
  #
76
- # code - String code returned to redirect_uri after authorization
77
- # redirect_uri - String URI returned to
686
+ # See http://www.cronofy.com/developers/api#token-issue for reference.
78
687
  #
79
- # Returns Cronofy::Credentials
80
- def get_token_from_code(code, redirect_uri)
81
- @auth.get_token_from_code(code, redirect_uri)
688
+ # Returns a set of Cronofy::Credentials for the account.
689
+ #
690
+ # Raises Cronofy::BadRequestError if the code is unknown, has been revoked,
691
+ # or the code and redirect URL do not match.
692
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
693
+ # not valid.
694
+ def get_token_from_code(code, redirect_url)
695
+ @auth.get_token_from_code(code, redirect_url)
82
696
  end
83
697
 
84
- # Public : Refreshes the access_token and periodically the refresh_token for authorization
85
- # see http://www.cronofy.com/developers/api#token-refresh
698
+ # Public: Refreshes the credentials for the account's access token.
699
+ #
700
+ # Usually called in response to a Cronofy::AuthenticationFailureError as
701
+ # these usually occur when the access token has expired and needs
702
+ # refreshing.
86
703
  #
87
- # Returns Cronofy::Credentials
704
+ # See http://www.cronofy.com/developers/api#token-refresh for reference.
705
+ #
706
+ # Returns a set of Cronofy::Credentials for the account.
707
+ #
708
+ # Raises Cronofy::BadRequestError if refresh token code is unknown or has
709
+ # been revoked.
710
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
711
+ # not valid.
712
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
88
713
  def refresh_access_token
89
714
  @auth.refresh!
90
715
  end
91
716
 
92
- private
717
+ # Public: Obtains access to an application calendar
718
+ #
719
+ # See http://www.cronofy.com/developers/alpha/api#application-calendar for reference.
720
+ #
721
+ # Returns a set of Cronofy::Credentials for the account.
722
+ #
723
+ # Raises Cronofy::BadRequestError if refresh token code is unknown or has
724
+ # been revoked.
725
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
726
+ # not valid.
727
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
728
+ def application_calendar(application_calendar_id)
729
+ @auth.application_calendar(application_calendar_id)
730
+ end
731
+
732
+ # Public: Revokes the account's refresh token and access token.
733
+ #
734
+ # After making this call the Client will become unusable. You should also
735
+ # delete the stored credentials used to create this instance.
736
+ #
737
+ # See http://www.cronofy.com/developers/api#revoke-authorization for
738
+ # reference.
739
+ #
740
+ # Returns nothing.
741
+ #
742
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
743
+ # not valid.
744
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
745
+ def revoke_authorization
746
+ @auth.revoke!
747
+ end
748
+
749
+ def revoke_by_sub(sub)
750
+ @auth.revoke_by_sub(sub)
751
+ end
752
+
753
+ def revoke_by_token(token)
754
+ @auth.revoke_by_token(token)
755
+ end
756
+
757
+ # Public: Requests elevated permissions for a set of calendars.
758
+ #
759
+ # args - A Hash of options used to initialize the request (default: {}):
760
+ # :permissions - An Array of calendar permission hashes to set on
761
+ # the each hash must contain symbols for both
762
+ # `calendar_id` and `permission_level`
763
+ # :redirect_uri - A uri to redirect the end user back to after they
764
+ # have either granted or rejected the request for
765
+ # elevated permission.
766
+ #
767
+ # In the case of normal accounts:
768
+ # After making this call the end user will have to grant the extended
769
+ # permissions to their calendar via rhe url returned from the response.
770
+ #
771
+ # In the case of service accounts:
772
+ # After making this call the exteneded permissions will be granted provided
773
+ # the relevant scope has been granted to the account
774
+ #
775
+ # Returns a extended permissions response.
776
+ #
777
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
778
+ # not valid.
779
+ def elevated_permissions(args = {})
780
+ filtered_permissions = args[:permissions].map do |permission|
781
+ { calendar_id: permission[:calendar_id], permission_level: permission[:permission_level] }
782
+ end
783
+
784
+ body = { permissions: filtered_permissions }
785
+ body[:redirect_uri] = args[:redirect_uri] if args[:redirect_uri]
786
+
787
+ response = post("/v1/permissions", body)
788
+ parse_json(PermissionsResponse, "permissions_request", response)
789
+ end
790
+
791
+ # Public: Lists all the resources for the service account.
792
+ #
793
+ # Returns an Array of Resources.
794
+ #
795
+ # Raises Cronofy::CredentialsMissingError if no credentials available
796
+ # or if non-service account credentials are provided.
797
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
798
+ # longer valid.
799
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
800
+ # include the required scope.
801
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
802
+ # limits for the application.
803
+ def resources
804
+ response = get("/v1/resources")
805
+ parse_collection(Resource, "resources", response)
806
+ end
807
+
808
+ # Public: Performs an availability query.
809
+ #
810
+ # options - The Hash options used to refine the selection (default: {}):
811
+ # :participants - An Array of participant groups or a Hash
812
+ # for a single participant group.
813
+ # :required_duration - An Integer representing the minimum number
814
+ # of minutes of availability required.
815
+ # :query_periods - An Array of available time periods Hashes,
816
+ # each must specify a start and end Time.
817
+ # :start_interval - An Integer representing the start interval
818
+ # of minutes for the availability query.
819
+ # :buffer - An Hash containing the buffer to apply to
820
+ # the availability query.
821
+ #
822
+ # Returns an Array of AvailablePeriods.
823
+ #
824
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
825
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
826
+ # longer valid.
827
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
828
+ # include the required scope.
829
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
830
+ # parameters.
831
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
832
+ # limits for the application.
833
+ def availability(options = {})
834
+ options[:participants] = map_availability_participants(options[:participants])
835
+ options[:required_duration] = map_availability_required_duration(options[:required_duration])
93
836
 
94
- ERROR_MAP = {
95
- 401 => ::Cronofy::AuthenticationFailureError,
96
- 403 => ::Cronofy::AuthorizationFailureError,
97
- 404 => ::Cronofy::NotFoundError,
98
- 422 => ::Cronofy::InvalidRequestError,
99
- 429 => ::Cronofy::TooManyRequestsError
100
- }
837
+ if options[:start_interval]
838
+ options[:start_interval] = map_availability_required_duration(options[:start_interval])
839
+ end
101
840
 
102
- def do_request(&block)
103
- begin
104
- block.call
105
- rescue OAuth2::Error => e
106
- error_class = ERROR_MAP.fetch(e.response.status, UnknownError)
107
- raise error_class.new(e.response.headers['status'], e.response)
841
+ if buffer = options[:buffer]
842
+ options[:buffer] = map_availability_buffer(buffer)
108
843
  end
844
+
845
+ translate_available_periods(options[:query_periods] || options[:available_periods])
846
+
847
+ response = availability_post("/v1/availability", options)
848
+
849
+ parse_collections(
850
+ response,
851
+ available_periods: AvailablePeriod,
852
+ available_slots: AvailableSlot,
853
+ )
109
854
  end
110
855
 
111
- end
856
+ # Public: Performs an sequenced availability query.
857
+ #
858
+ # options - The Hash options used to refine the selection (default: {}):
859
+ # :sequence - An Array of sequence defintions containing
860
+ # a Hash of:
861
+ # :sequence_id - A String to uniquely identify this part
862
+ # of the proposed sequence.
863
+ # :ordinal - An integer to define the ordering of the
864
+ # proposed sequence. (Optional)
865
+ # :participants - An Array of participant groups or a Hash
866
+ # for a single participant group.
867
+ # :required_duration - An Integer representing the minimum
868
+ # number of minutes of availability required.
869
+ # :start_interval - An Integer representing the start interval
870
+ # of minutes for the availability query.
871
+ # :buffer - An Hash containing the buffer to apply to
872
+ # the availability query.
873
+ # :query_periods - An Array of available time periods Hashes,
874
+ # each must specify a start and end Time.
875
+ #
876
+ # Returns an Array of Sequences.
877
+ #
878
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
879
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
880
+ # longer valid.
881
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
882
+ # include the required scope.
883
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
884
+ # parameters.
885
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
886
+ # limits for the application.
887
+ def sequenced_availability(options = {})
888
+ options[:sequence] = map_availability_sequence(options[:sequence])
112
889
 
113
- # Alias for backwards compatibility
114
- # depcrectated will be removed
115
- class Cronofy < Client
890
+ translate_available_periods(options[:query_periods] || options[:available_periods])
891
+
892
+ response = availability_post("/v1/sequenced_availability", options)
893
+ parse_collection(Sequence, "sequences", response)
894
+ end
116
895
 
896
+ # Public: Generates an add to calendar link to start the OAuth process with
897
+ # an event to be automatically upserted
898
+ #
899
+ # oauth - A Hash describing the OAuth flow required:
900
+ # :scope - A String representing the scopes to ask for
901
+ # within the OAuth flow
902
+ # :redirect_uri - A String containing a url to redirect the
903
+ # user to after completing the OAuth flow.
904
+ # :scope - A String representing additional state to
905
+ # be passed within the OAuth flow.
906
+ #
907
+ # event - A Hash describing the event with symbolized keys:
908
+ # :event_id - A String uniquely identifying the event for
909
+ # your application (note: this is NOT an ID
910
+ # generated by Cronofy).
911
+ # :summary - A String to use as the summary, sometimes
912
+ # referred to as the name or title, of the
913
+ # event.
914
+ # :description - A String to use as the description, sometimes
915
+ # referred to as the notes or body, of the
916
+ # event.
917
+ # :start - The Time or Date the event starts.
918
+ # :end - The Time or Date the event ends.
919
+ # :url - The URL associated with the event.
920
+ # :location - A Hash describing the location of the event
921
+ # with symbolized keys (optional):
922
+ # :description - A String describing the
923
+ # location.
924
+ # :lat - A String of the location's latitude.
925
+ # :long - A String of the location's longitude.
926
+ # :reminders - An Array of Hashes describing the desired
927
+ # reminders for the event. Reminders should be
928
+ # specified in priority order as, for example,
929
+ # when the underlying provider only supports a
930
+ # single reminder then the first reminder will
931
+ # be used.
932
+ # :minutes - An Integer specifying the number
933
+ # of minutes before the start of the
934
+ # event that the reminder should
935
+ # occur.
936
+ # :transparency - The transparency state for the event (optional).
937
+ # Accepted values are "transparent" and "opaque".
938
+ # :attendees - A Hash of :invite and :reject, each of which is
939
+ # an array of invitees to invite to or reject from
940
+ # the event. Invitees are represented by a hash of
941
+ # :email and :display_name (optional).
942
+ #
943
+ # Example
944
+ #
945
+ # client.add_to_calendar(
946
+ # oauth: {
947
+ # scopes: 'read_events delete_events',
948
+ # redirect_uri: 'http://www.example.com',
949
+ # state: 'example_state'
950
+ # },
951
+ # event: {
952
+ # event_id: "qTtZdczOccgaPncGJaCiLg",
953
+ # summary: "Board meeting",
954
+ # description: "Discuss plans for the next quarter.",
955
+ # start: Time.utc(2014, 8, 5, 15, 30),
956
+ # end: Time.utc(2014, 8, 5, 17, 30),
957
+ # location: {
958
+ # description: "Board room",
959
+ # lat: "1.2345",
960
+ # long: "0.1234"
961
+ # })
962
+ #
963
+ # See http://www.cronofy.com/developers/api#upsert-event for reference.
964
+ #
965
+ # Returns a AddToCalendarResponse.
966
+ #
967
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
968
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
969
+ # longer valid.
970
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
971
+ # include the required scope.
972
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
973
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
974
+ # parameters.
975
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
976
+ # limits for the application.
977
+ def add_to_calendar(args = {})
978
+ body = args.merge(client_id: @client_id, client_secret: @client_secret)
979
+
980
+ body[:event][:start] = encode_event_time(body[:event][:start])
981
+ body[:event][:end] = encode_event_time(body[:event][:end])
982
+
983
+ response = post("/v1/add_to_calendar", body)
984
+ parse_json(AddToCalendarResponse, nil , response)
985
+ end
986
+
987
+ # Public: Generates an real time scheduling link to start the OAuth process with
988
+ # an event to be automatically upserted
989
+ #
990
+ # oauth - A Hash describing the OAuth flow required:
991
+ # :scope - A String representing the scopes to ask for
992
+ # within the OAuth flow
993
+ # :redirect_uri - A String containing a url to redirect the
994
+ # user to after completing the OAuth flow.
995
+ # :scope - A String representing additional state to
996
+ # be passed within the OAuth flow.
997
+ #
998
+ # event - A Hash describing the event with symbolized keys:
999
+ # :event_id - A String uniquely identifying the event for
1000
+ # your application (note: this is NOT an ID
1001
+ # generated by Cronofy).
1002
+ # :summary - A String to use as the summary, sometimes
1003
+ # referred to as the name or title, of the
1004
+ # event.
1005
+ # :description - A String to use as the description, sometimes
1006
+ # referred to as the notes or body, of the
1007
+ # event.
1008
+ # :url - The URL associated with the event.
1009
+ # :location - A Hash describing the location of the event
1010
+ # with symbolized keys (optional):
1011
+ # :description - A String describing the
1012
+ # location.
1013
+ # :lat - A String of the location's latitude.
1014
+ # :long - A String of the location's longitude.
1015
+ # :reminders - An Array of Hashes describing the desired
1016
+ # reminders for the event. Reminders should be
1017
+ # specified in priority order as, for example,
1018
+ # when the underlying provider only supports a
1019
+ # single reminder then the first reminder will
1020
+ # be used.
1021
+ # :minutes - An Integer specifying the number
1022
+ # of minutes before the start of the
1023
+ # event that the reminder should
1024
+ # occur.
1025
+ # :transparency - The transparency state for the event (optional).
1026
+ # Accepted values are "transparent" and "opaque".
1027
+ # :attendees - A Hash of :invite and :reject, each of which is
1028
+ # an array of invitees to invite to or reject from
1029
+ # the event. Invitees are represented by a hash of
1030
+ # :email and :display_name (optional).
1031
+ # availability - A Hash describing the availability details for the event:
1032
+ # :participants - A hash stating who is required for the availability
1033
+ # call
1034
+ # :required_duration - A hash stating the length of time the event will
1035
+ # last for
1036
+ # :query_periods - A hash stating the available periods for the event
1037
+ # :start_interval - An Integer representing the start interval
1038
+ # of minutes for the availability query.
1039
+ # :buffer - An Hash containing the buffer to apply to
1040
+ # the availability query.
1041
+ # target_calendars - An array of hashes stating into which calendars to insert the created
1042
+ # event
1043
+ #
1044
+ # Examples
1045
+ #
1046
+ # - Availability example
1047
+ # client.add_to_calendar(
1048
+ # oauth: {
1049
+ # redirect_uri: 'http://www.example.com'
1050
+ # },
1051
+ # event: {
1052
+ # event_id: "qTtZdczOccgaPncGJaCiLg",
1053
+ # summary: "Board meeting",
1054
+ # description: "Discuss plans for the next quarter.",
1055
+ # location: {
1056
+ # description: "Board room",
1057
+ # lat: "1.2345",
1058
+ # long: "0.1234"
1059
+ # }
1060
+ # },
1061
+ # availability: {
1062
+ # participants: [
1063
+ # {
1064
+ # members: [{
1065
+ # sub: "acc_567236000909002",
1066
+ # calendar_ids: ["cal_n23kjnwrw2_jsdfjksn234"]
1067
+ # }],
1068
+ # required: 'all'
1069
+ # }
1070
+ # ],
1071
+ # required_duration: { minutes: 60 },
1072
+ # query_periods: [{
1073
+ # start: Time.utc(2017, 1, 1, 9, 00),
1074
+ # end: Time.utc(2017, 1, 1, 17, 00),
1075
+ # }]
1076
+ # },
1077
+ # target_calendars: [{
1078
+ # sub: "acc_567236000909002",
1079
+ # calendar_id: "cal_n23kjnwrw2_jsdfjksn234"
1080
+ # }]
1081
+ # )
1082
+ #
1083
+ # See http://www.cronofy.com/developers/api#real-time-scheduling for reference.
1084
+ #
1085
+ # Returns a AddToCalendarResponse.
1086
+ #
1087
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1088
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
1089
+ # longer valid.
1090
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
1091
+ # include the required scope.
1092
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
1093
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1094
+ # parameters.
1095
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1096
+ # limits for the application.
1097
+ def real_time_scheduling(args = {})
1098
+ body = args.merge(client_id: @client_id, client_secret: @client_secret)
1099
+
1100
+ if availability = args[:availability]
1101
+ availability[:participants] = map_availability_participants(availability[:participants])
1102
+ availability[:required_duration] = map_availability_required_duration(availability[:required_duration])
1103
+
1104
+ if value = availability[:start_interval]
1105
+ availability[:start_interval] = map_availability_required_duration(value)
1106
+ end
1107
+
1108
+ if buffer = availability[:buffer]
1109
+ availability[:buffer] = map_availability_buffer(buffer)
1110
+ end
1111
+ end
1112
+
1113
+ translate_available_periods(availability[:query_periods] || availability[:available_periods])
1114
+ body[:availability] = availability
1115
+
1116
+ response = raw_post("/v1/real_time_scheduling", body)
1117
+ parse_json(AddToCalendarResponse, nil , response)
1118
+ end
1119
+
1120
+ # Public: Gets the status of a Real-Time Scheduling link.
1121
+ #
1122
+ # Provide one of the following arguments to identify the link:
1123
+ # id - A String uniquely identifying the link, returned on initial
1124
+ # creation
1125
+ # token - The token portion of the link's URL
1126
+ #
1127
+ # See https://docs.cronofy.com/developers/api/scheduling/real-time-scheduling/status/ for reference.
1128
+ #
1129
+ # Returns a RealTimeSchedulingStatus.
1130
+ #
1131
+ # Raises ArgumentError if neither 'id' nor 'token' arguments are passed.
1132
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1133
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1134
+ # limits for the application.
1135
+ def get_real_time_scheduling_status(args = {})
1136
+ if args[:token]
1137
+ url = "/v1/real_time_scheduling?token=#{args[:token]}"
1138
+ elsif args[:id]
1139
+ url = "/v1/real_time_scheduling/#{args[:id]}"
1140
+ else
1141
+ raise ArgumentError.new("Must pass either token or id argument")
1142
+ end
1143
+
1144
+ response = wrapped_request { api_key!.get(url) }
1145
+ parse_json(RealTimeSchedulingStatus, 'real_time_scheduling' , response)
1146
+ end
1147
+
1148
+ # Public: Disables a Real-Time Scheduling link.
1149
+ #
1150
+ # id - A String uniquely identifying the link, returned
1151
+ # on initial creation
1152
+ # display_message - A message to display to visitors of the disabled link
1153
+ #
1154
+ # See https://docs.cronofy.com/developers/api/scheduling/real-time-scheduling/disable/ for reference.
1155
+ #
1156
+ # Returns a RealTimeSchedulingStatus.
1157
+ #
1158
+ # Raises ArgumentError if no 'id' argument is passed.
1159
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1160
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1161
+ # parameters.
1162
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1163
+ # limits for the application.
1164
+ def disable_real_time_scheduling(args = {})
1165
+ id = args.delete(:id)
1166
+
1167
+ raise ArgumentError.new('id argument is required') unless id
1168
+
1169
+ response = wrapped_request { api_key!.post("/v1/real_time_scheduling/#{id}/disable", json_request_args(args)) }
1170
+ parse_json(RealTimeSchedulingStatus, 'real_time_scheduling' , response)
1171
+ end
1172
+
1173
+ # Public: Generates an real time sequencing link to start the OAuth process with
1174
+ # an event to be automatically upserted
1175
+ #
1176
+ # oauth - A Hash describing the OAuth flow required:
1177
+ # :scope - A String representing the scopes to ask for
1178
+ # within the OAuth flow
1179
+ # :redirect_uri - A String containing a url to redirect the
1180
+ # user to after completing the OAuth flow.
1181
+ # :scope - A String representing additional state to
1182
+ # be passed within the OAuth flow.
1183
+ #
1184
+ # availability - A Hash describing the availability details for the event:
1185
+ # :sequence - An Array of sequence defintions containing
1186
+ # a Hash of:
1187
+ # :sequence_id - A String to uniquely identify this part
1188
+ # of the proposed sequence.
1189
+ # :ordinal - An integer to define the ordering of the
1190
+ # proposed sequence. (Optional)
1191
+ # :participants - An Array of participant groups or a Hash
1192
+ # for a single participant group.
1193
+ # :required_duration - An Integer representing the minimum
1194
+ # number of minutes of availability required.
1195
+ # :start_interval - An Integer representing the start interval
1196
+ # of minutes for the availability query.
1197
+ # :buffer - An Hash containing the buffer to apply to
1198
+ # the availability query.
1199
+ # :event - A Hash describing the event:
1200
+ # :event_id - A String uniquely identifying the event for
1201
+ # your application (note: this is NOT an ID
1202
+ # generated by Cronofy).
1203
+ # :summary - A String to use as the summary, sometimes
1204
+ # referred to as the name or title, of the
1205
+ # event.
1206
+ # :description - A String to use as the description, sometimes
1207
+ # referred to as the notes or body, of the
1208
+ # event.
1209
+ # :url - The URL associated with the event.
1210
+ # :location - A Hash describing the location of the event
1211
+ # with symbolized keys (optional):
1212
+ # :description - A String describing the
1213
+ # location.
1214
+ # :lat - A String of the location's latitude.
1215
+ # :long - A String of the location's longitude.
1216
+ # :reminders - An Array of Hashes describing the desired
1217
+ # reminders for the event. Reminders should be
1218
+ # specified in priority order as, for example,
1219
+ # when the underlying provider only supports a
1220
+ # single reminder then the first reminder will
1221
+ # be used.
1222
+ # :minutes - An Integer specifying the number
1223
+ # of minutes before the start of the
1224
+ # event that the reminder should
1225
+ # occur.
1226
+ # :transparency - The transparency state for the event (optional).
1227
+ # Accepted values are "transparent" and "opaque".
1228
+ # :attendees - A Hash of :invite and :reject, each of which is
1229
+ # an array of invitees to invite to or reject from
1230
+ # the event. Invitees are represented by a hash of
1231
+ # :email and :display_name (optional).
1232
+ # :query_periods - A hash stating the query periods for the event
1233
+ # target_calendars - An array of hashes stating into which calendars to insert the created
1234
+ # event
1235
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1236
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
1237
+ # longer valid.
1238
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
1239
+ # include the required scope.
1240
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
1241
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1242
+ # parameters.
1243
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1244
+ # limits for the application.
1245
+ def real_time_sequencing(args)
1246
+ body = args.merge(client_id: @client_id, client_secret: @client_secret)
1247
+
1248
+ if availability = args[:availability]
1249
+ availability[:sequence] = map_availability_sequence(availability[:sequence])
1250
+ periods = availability[:query_periods] || availability[:available_periods]
1251
+ translate_available_periods(periods) if periods
1252
+ end
1253
+
1254
+ body[:availability] = availability
1255
+
1256
+ response = raw_post("/v1/real_time_sequencing", body)
1257
+ parse_json(AddToCalendarResponse, nil , response)
1258
+ end
1259
+
1260
+ # Public: Creates a link_token to allow explicitly linking of an account
1261
+ #
1262
+ # See https://www.cronofy.com/developers/api/alpha/#auth-explicit-linking for
1263
+ # reference.
1264
+ #
1265
+ # Returns a link token
1266
+ #
1267
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1268
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
1269
+ # longer valid.
1270
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1271
+ # limits for the application.
1272
+ def link_token
1273
+ response = post("/v1/link_tokens", nil)
1274
+ parse_json(String, 'link_token', response)
1275
+ end
1276
+
1277
+ # Public: Revokes the authorization to the given profile.
1278
+ #
1279
+ # See https://www.cronofy.com/developers/api/alpha/#revoke-profile for
1280
+ # reference.
1281
+ #
1282
+ # Returns nothing.
1283
+ #
1284
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1285
+ def revoke_profile_authorization(profile_id)
1286
+ post("/v1/profiles/#{profile_id}/revoke", nil)
1287
+ nil
1288
+ end
1289
+
1290
+ # Public: Creates or updates smart invite.
1291
+ #
1292
+ # smart_invite_id - A String uniquely identifying the event for your
1293
+ # application (note: this is NOT an ID generated
1294
+ # by Cronofy).
1295
+ # callback_url - The URL within your application you want Cronofy to
1296
+ # send notifications to about user interactions with
1297
+ # the Smart Invite.
1298
+ # recipient - A Hash containing the intended recipient of the invite
1299
+ # :email - A String for the email address you are
1300
+ # going to send the Smart Invite to.
1301
+ # event - A Hash describing the event with symbolized keys:
1302
+ # :summary - A String to use as the summary, sometimes
1303
+ # referred to as the name or title, of the
1304
+ # event.
1305
+ # :description - A String to use as the description, sometimes
1306
+ # referred to as the notes or body, of the
1307
+ # event.
1308
+ # :start - The Time or Date the event starts.
1309
+ # :end - The Time or Date the event ends.
1310
+ # :url - The URL associated with the event.
1311
+ # :location - A Hash describing the location of the event
1312
+ # with symbolized keys (optional):
1313
+ # :description - A String describing the
1314
+ # location.
1315
+ # :lat - A String of the location's latitude.
1316
+ # :long - A String of the location's longitude.
1317
+ # :reminders - An Array of Hashes describing the desired
1318
+ # reminders for the event. Reminders should be
1319
+ # specified in priority order as, for example,
1320
+ # when the underlying provider only supports a
1321
+ # single reminder then the first reminder will
1322
+ # be used.
1323
+ # :minutes - An Integer specifying the number
1324
+ # of minutes before the start of the
1325
+ # event that the reminder should
1326
+ # occur.
1327
+ # :transparency - The transparency state for the event (optional).
1328
+ # Accepted values are "transparent" and "opaque".
1329
+ # :color - The color of the event (optional).
1330
+ #
1331
+ # organizer - A Hash containing the details of the organizer.
1332
+ # :name - A String value for the display name of the
1333
+ # event organizer
1334
+ # Examples
1335
+ #
1336
+ # client.upsert_smart_invite(
1337
+ # smart_invite_id: "qTtZdczOccgaPncGJaCiLg",
1338
+ # callback_url: "http://www.example.com",
1339
+ # recipient: {
1340
+ # email: "example@example.com"
1341
+ # },
1342
+ # event: {
1343
+ # summary: "Board meeting",
1344
+ # description: "Discuss plans for the next quarter.",
1345
+ # start: Time.utc(2014, 8, 5, 15, 30),
1346
+ # end: Time.utc(2014, 8, 5, 17, 30),
1347
+ # location: {
1348
+ # description: "Board room",
1349
+ # lat: "1.2345",
1350
+ # long: "0.1234"
1351
+ # },
1352
+ # organizer: {
1353
+ # name: "Smart invite application"
1354
+ # }
1355
+ # )
1356
+ #
1357
+ # See http://www.cronofy.com/developers/alpha/api#smart-invite for reference.
1358
+ #
1359
+ # Returns a SmartInviteResponse.
1360
+ #
1361
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1362
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1363
+ # parameters.
1364
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1365
+ # limits for the application.
1366
+ def upsert_smart_invite(body={})
1367
+ body[:event][:start] = encode_event_time(body[:event][:start])
1368
+ body[:event][:end] = encode_event_time(body[:event][:end])
1369
+
1370
+ response = wrapped_request { api_key!.post("/v1/smart_invites", json_request_args(body)) }
1371
+ parse_json(SmartInviteResponse, nil, response)
1372
+ end
1373
+
1374
+
1375
+ # Public: Cancels a smart invite
1376
+ #
1377
+ # smart_invite_id - A String uniquely identifying the event for your
1378
+ # application (note: this is NOT an ID generated
1379
+ # by Cronofy).
1380
+ # recipient - A Hash containing the intended recipient of the invite
1381
+ # :email - A String for thee email address you are
1382
+ # going to send the Smart Invite to.
1383
+ #
1384
+ # See http://www.cronofy.com/developers/alpha/api#smart-invite for reference.
1385
+ #
1386
+ # Returns a SmartInviteResponse.
1387
+ #
1388
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1389
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1390
+ # parameters.
1391
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1392
+ # limits for the application.
1393
+ def cancel_smart_invite(body={})
1394
+ body[:method] = 'cancel'
1395
+ response = wrapped_request { api_key!.post("/v1/smart_invites", json_request_args(body)) }
1396
+ parse_json(SmartInviteResponse, nil, response)
1397
+ end
1398
+
1399
+ # Public: Removes an individual recipient from a multiple recipient smart invite
1400
+ #
1401
+ # smart_invite_id - A String uniquely identifying the event for your
1402
+ # application (note: this is NOT an ID generated
1403
+ # by Cronofy).
1404
+ #
1405
+ # recipient - A Hash containing the recipient to be removed
1406
+ # :email - A String for the email address of
1407
+ # the recipient to remove.
1408
+ #
1409
+ # See https://docs.cronofy.com/developers/api-alpha/smart-invites/multiple-recipients/#remove-invite-recipient
1410
+ # for reference.
1411
+ #
1412
+ # Returns a SmartInviteResponse
1413
+ #
1414
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1415
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1416
+ # parameters.
1417
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1418
+ # limits for the application.
1419
+ def remove_recipient_smart_invite(body={})
1420
+ body[:method] = 'remove'
1421
+ response = wrapped_request { api_key!.post("/v1/smart_invites", json_request_args(body)) }
1422
+ parse_json(SmartInviteResponse, nil, response)
1423
+ end
1424
+
1425
+ # Public: Gets the details for a smart invite.
1426
+ #
1427
+ # smart_invite_id - A String uniquely identifying the event for your
1428
+ # application (note: this is NOT an ID generated
1429
+ # by Cronofy).
1430
+ # recipient_email - The email address for the recipient to get details for.
1431
+ #
1432
+ # See http://www.cronofy.com/developers/alpha/api#smart-invite for reference.
1433
+ #
1434
+ # Returns a SmartInviteResponse.
1435
+ #
1436
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1437
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1438
+ # parameters.
1439
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1440
+ # limits for the application.
1441
+ def get_smart_invite(smart_invite_id, recipient_email)
1442
+ response = wrapped_request { api_key!.get("/v1/smart_invites?recipient_email=#{recipient_email}&smart_invite_id=#{smart_invite_id}") }
1443
+ parse_json(SmartInviteResponse, nil, response)
1444
+ end
1445
+
1446
+ # Public: Creates an element_token to pass to a UI Element
1447
+ #
1448
+ # options - A Hash of options for the token
1449
+ # :permissions - An Array of strings describing the
1450
+ # permissions required for the token
1451
+ # :subs - An Array of sub values for the account(s)
1452
+ # the element will be accessing
1453
+ # :origin - The scheme://hostname where the token will
1454
+ # be used.
1455
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
1456
+ #
1457
+ # See https://docs.cronofy.com/developers/ui-elements/authentication for
1458
+ # reference.
1459
+ #
1460
+ # Returns an element token
1461
+ #
1462
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1463
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
1464
+ # longer valid.
1465
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1466
+ # limits for the application.
1467
+ def element_token(options)
1468
+ response = wrapped_request { api_key!.post("/v1/element_tokens", json_request_args(options)) }
1469
+ parse_json(ElementToken, "element_token", response)
1470
+ end
1471
+
1472
+ # Public: Creates or updates an AvailabilityRule.
1473
+ #
1474
+ # availability_rule_id - A String uniquely identifying the availability rule
1475
+ # for the authenticated user in your application
1476
+ # (note: this is NOT an ID generated by Cronofy).
1477
+ # tzid - The time zone identifier for the rule.
1478
+ # calendar_ids - An optional array of calendar_ids that should impact the
1479
+ # user's availability.
1480
+ # weekly_periods - An array of objects describing a weekly recurring available period
1481
+ # :day - A String for the week day
1482
+ # :start_time - A String for 24hr time that period starts eg: 09:30
1483
+ # :end_time - A String for 24hr time that period ends eg: 17:30
1484
+ # Examples
1485
+ #
1486
+ # client.upsert_availability_rule(
1487
+ # availability_rule_id: "qTtZdczOccgaPncGJaCiLg",
1488
+ # tzid: "America/Chicago",
1489
+ # calendar_ids: [
1490
+ # "cal_n23kjnwrw2_jsdfjksn234"
1491
+ # ],
1492
+ # weekly_periods: [
1493
+ # {
1494
+ # day: "monday",
1495
+ # start_time: "09:30",
1496
+ # end_time: "12:30",
1497
+ # },
1498
+ # {
1499
+ # day: "tuesday",
1500
+ # start_time: "09:30",
1501
+ # end_time: "12:30",
1502
+ # }
1503
+ # ]
1504
+ # )
1505
+ #
1506
+ # See http://www.cronofy.com/developers/alpha/api#availability_rules for reference.
1507
+ #
1508
+ # Returns an AvailabilityRuleResponse.
1509
+ #
1510
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1511
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1512
+ # parameters.
1513
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1514
+ # limits for the application.
1515
+ def upsert_availability_rule(body)
1516
+ response = wrapped_request { post("/v1/availability_rules", body) }
1517
+ parse_json(AvailabilityRule, 'availability_rule', response)
1518
+ end
1519
+
1520
+ # Public: Gets an AvailabilityRule.
1521
+ #
1522
+ # availability_rule_id - A String uniquely identifying the availability rule
1523
+ # for the authenticated user in your application
1524
+ #
1525
+ # Returns an AvailabilityRuleResponse.
1526
+ #
1527
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1528
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1529
+ # parameters.
1530
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1531
+ # limits for the application.
1532
+ def get_availability_rule(availability_rule_id)
1533
+ response = wrapped_request { get("/v1/availability_rules/#{availability_rule_id}") }
1534
+ parse_json(AvailabilityRule, 'availability_rule', response)
1535
+ end
1536
+
1537
+ # Public: Gets all AvailabilityRules for an account.
1538
+ #
1539
+ # Returns an array of AvailabilityRules.
1540
+ #
1541
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1542
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1543
+ # parameters.
1544
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1545
+ # limits for the application.
1546
+ def get_availability_rules
1547
+ response = wrapped_request { get("/v1/availability_rules") }
1548
+ parse_collection(AvailabilityRule, 'availability_rules', response)
1549
+ end
1550
+
1551
+ # Public: Deletes an AvailabilityRule.
1552
+ #
1553
+ # availability_rule_id - A String uniquely identifying the availability rule
1554
+ # for the authenticated user in your application
1555
+ #
1556
+ # Returns nothing.
1557
+ #
1558
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1559
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1560
+ # parameters.
1561
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1562
+ # limits for the application.
1563
+ def delete_availability_rule(availability_rule_id)
1564
+ wrapped_request { delete("/v1/availability_rules/#{availability_rule_id}") }
1565
+ nil
1566
+ end
1567
+
1568
+ # Public: Creates or updates an AvailablePeriod.
1569
+ #
1570
+ # available_period_id - A String uniquely identifying the available period
1571
+ # for the authenticated user in your application
1572
+ # (note: this is NOT an ID generated by Cronofy).
1573
+ # body - A Hash describing the available period with
1574
+ # symbolized keys:
1575
+ # :start - A String (ISO-8601 date/time)
1576
+ # :end - A String (ISO-8601 date/time)
1577
+ #
1578
+ # See https://docs.cronofy.com/developers/api/scheduling/available-periods/upsert/
1579
+ # for reference.
1580
+ #
1581
+ # Returns nothing.
1582
+ #
1583
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1584
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1585
+ # parameters.
1586
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1587
+ # limits for the application.
1588
+ def upsert_available_period(available_period_id, body)
1589
+ payload = body.merge(available_period_id: available_period_id)
1590
+ wrapped_request { post("/v1/available_periods", payload) }
1591
+ nil
1592
+ end
1593
+
1594
+ # Public: Gets all AvailablePeriods for an account.
1595
+ #
1596
+ # options - The Hash options used to refine the selection (default: {}):
1597
+ # :from - The minimum Date from which to return periods
1598
+ # (optional).
1599
+ # :to - The Date to return periods up until (optional).
1600
+ # :tzid - A String representing a known time zone
1601
+ # identifier from the IANA Time Zone Database
1602
+ # (default: Etc/UTC).
1603
+ # :localized_times - A Boolean specifying whether the start and
1604
+ # end times should be returned with any
1605
+ # available localization information
1606
+ # (optional).
1607
+ #
1608
+ # Returns an array of AvailablePeriods.
1609
+ #
1610
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1611
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1612
+ # parameters.
1613
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1614
+ # limits for the application.
1615
+ def get_available_periods(options={})
1616
+ query = {}
1617
+ query[:from] = to_iso8601(options[:from]) if options[:from]
1618
+ query[:to] = to_iso8601(options[:to]) if options[:to]
1619
+ query[:tzid] = options[:tzid] if options[:tzid]
1620
+ query[:localized_times] = options[:localized_times] if options[:localized_times]
1621
+ if query.any?
1622
+ query_string = "?#{URI.encode_www_form(query)}"
1623
+ end
1624
+
1625
+ response = wrapped_request { get("/v1/available_periods#{query_string}") }
1626
+ parse_collection(AvailablePeriod, 'available_periods', response)
1627
+ end
1628
+
1629
+ # Public: Deletes an AvailablePeriod.
1630
+ #
1631
+ # available_period_id - A String uniquely identifying the available period
1632
+ # for the authenticated user in your application
1633
+ #
1634
+ # Returns nothing.
1635
+ #
1636
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
1637
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
1638
+ # parameters.
1639
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
1640
+ # limits for the application.
1641
+ def delete_available_period(available_period_id)
1642
+ wrapped_request { delete("/v1/available_periods", available_period_id: available_period_id) }
1643
+ nil
1644
+ end
1645
+
1646
+ # Public: Creates a scheduling conversation
1647
+ #
1648
+ # pre release end-point documentation to follow
1649
+ #
1650
+ def create_scheduling_conversation(body)
1651
+ response = wrapped_request { post("/v1/scheduling_conversations", body) }
1652
+ parse_json(SchedulingConversation, 'scheduling_conversation', response)
1653
+ end
1654
+
1655
+ # Public: Creates a scheduling conversation
1656
+ #
1657
+ # pre release end-point documentation to follow
1658
+ #
1659
+ def get_scheduling_conversation(id)
1660
+ response = wrapped_request { get("/v1/scheduling_conversations/#{id}") }
1661
+ parse_json(SchedulingConversation, 'scheduling_conversation', response)
1662
+ end
1663
+
1664
+ # Public: Looks up a scheduling conversation with a token returned by a redirect
1665
+ #
1666
+ # pre release end-point documentation to follow
1667
+ #
1668
+ def lookup_scheduling_conversation(token)
1669
+ response = wrapped_request { get("/v1/scheduling_conversations?token=#{token}") }
1670
+ parse_json(SchedulingConversationResponse, nil, response)
1671
+ end
1672
+
1673
+ # Public: List available slots for a scheduling conversation
1674
+ #
1675
+ # pre release end-point documentation to follow
1676
+ #
1677
+ def list_scheduling_conversation_participant_slots(url)
1678
+ response = wrapped_request { get(url) }
1679
+ parse_collection(SchedulingConversationSlot, "slots", response )
1680
+ end
1681
+
1682
+ # Public: Choose one or more slots for a scheduling conversation
1683
+ #
1684
+ # pre release end-point documentation to follow
1685
+ #
1686
+ def select_scheduling_conversation_participant_slots(url, args)
1687
+ response = wrapped_request { post(url, args)}
1688
+ parse_json(SchedulingConversation, 'scheduling_conversation', response)
1689
+ end
1690
+
1691
+ private
1692
+
1693
+ def translate_available_periods(periods)
1694
+ periods.each do |params|
1695
+ AVAILABLE_PERIODS_TIME_PARAMS.select { |tp| params.key?(tp) }.each do |tp|
1696
+ params[tp] = to_iso8601(params[tp])
1697
+ end
1698
+ end
1699
+ end
1700
+
1701
+ def map_availability_participants(participants)
1702
+ case participants
1703
+ when Hash
1704
+ # Allow one group to be specified without being nested
1705
+ [map_availability_participants_group(participants)]
1706
+ when Enumerable
1707
+ participants.map do |group|
1708
+ map_availability_participants_group(group)
1709
+ end
1710
+ else
1711
+ participants
1712
+ end
1713
+ end
1714
+
1715
+ def map_availability_participants_group(participants)
1716
+ case participants
1717
+ when Hash
1718
+ participants[:members].map! do |member|
1719
+ map_availability_member(member)
1720
+ end
1721
+
1722
+ unless participants.key?(:required)
1723
+ participants[:required] = :all
1724
+ end
1725
+
1726
+ participants
1727
+ when Array
1728
+ participants.map do |group|
1729
+ map_availability_participants(group)
1730
+ end
1731
+ else
1732
+ participants
1733
+ end
1734
+ end
1735
+
1736
+ def map_availability_member(member)
1737
+ case member
1738
+ when String
1739
+ { sub: member }
1740
+ when Hash
1741
+ if member[:available_periods]
1742
+ translate_available_periods(member[:available_periods])
1743
+ end
1744
+ member
1745
+ else
1746
+ member
1747
+ end
1748
+ end
1749
+
1750
+ def map_availability_required_duration(required_duration)
1751
+ case required_duration
1752
+ when Integer
1753
+ { minutes: required_duration }
1754
+ else
1755
+ required_duration
1756
+ end
1757
+ end
1758
+
1759
+ def map_availability_buffer(buffer)
1760
+ result = {}
1761
+
1762
+ unless buffer.is_a?(Hash)
1763
+ return result
1764
+ end
1765
+
1766
+ if before_buffer = buffer[:before]
1767
+ result[:before] = map_buffer_details(before_buffer)
1768
+ end
1769
+
1770
+ if after_buffer = buffer[:after]
1771
+ result[:after] = map_buffer_details(after_buffer)
1772
+ end
1773
+
1774
+ result
1775
+ end
1776
+
1777
+ def map_buffer_details(buffer)
1778
+ result = map_availability_required_duration(buffer)
1779
+
1780
+ if minimum_buffer = buffer[:minimum]
1781
+ result[:minimum] = map_availability_required_duration(minimum_buffer)
1782
+ end
1783
+
1784
+ if maximum_buffer = buffer[:maximum]
1785
+ result[:maximum] = map_availability_required_duration(maximum_buffer)
1786
+ end
1787
+
1788
+ result
1789
+ end
1790
+
1791
+ def map_availability_sequence(sequence)
1792
+ case sequence
1793
+ when Enumerable
1794
+ sequence.map do |sequence_item|
1795
+ hash = {}
1796
+
1797
+ if value = sequence_item[:participants]
1798
+ hash[:participants] = map_availability_participants(value)
1799
+ end
1800
+
1801
+ if value = sequence_item[:required_duration]
1802
+ hash[:required_duration] = map_availability_required_duration(value)
1803
+ end
1804
+
1805
+ periods = sequence_item[:query_periods] || sequence_item[:available_periods]
1806
+
1807
+ if periods
1808
+ translate_available_periods(periods)
1809
+ end
1810
+
1811
+ if value = sequence_item[:start_interval]
1812
+ hash[:start_interval] = map_availability_required_duration(value)
1813
+ end
1814
+
1815
+ if buffer = sequence_item[:buffer]
1816
+ hash[:buffer] = map_availability_buffer(buffer)
1817
+ end
1818
+
1819
+ sequence_item.merge(hash)
1820
+ end
1821
+ else
1822
+ sequence
1823
+ end
1824
+ end
1825
+
1826
+ AVAILABLE_PERIODS_TIME_PARAMS = %i{
1827
+ start
1828
+ end
1829
+ }.freeze
1830
+
1831
+ FREE_BUSY_DEFAULT_PARAMS = { tzid: "Etc/UTC" }.freeze
1832
+ FREE_BUSY_TIME_PARAMS = %i{
1833
+ from
1834
+ to
1835
+ }.freeze
1836
+
1837
+ READ_EVENTS_DEFAULT_PARAMS = { tzid: "Etc/UTC" }.freeze
1838
+ READ_EVENTS_TIME_PARAMS = %i{
1839
+ from
1840
+ to
1841
+ last_modified
1842
+ }.freeze
1843
+
1844
+ def access_token!
1845
+ raise CredentialsMissingError.new unless @auth.access_token
1846
+ @auth.access_token
1847
+ end
1848
+
1849
+ def api_key!
1850
+ raise CredentialsMissingError.new unless @auth.api_key
1851
+ @auth.api_key
1852
+ end
1853
+
1854
+ def get(url, opts = {})
1855
+ wrapped_request { access_token!.get(url, opts) }
1856
+ end
1857
+
1858
+ def post(url, body)
1859
+ wrapped_request { access_token!.post(url, json_request_args(body)) }
1860
+ end
1861
+
1862
+ def delete(url, body = nil)
1863
+ wrapped_request { access_token!.delete(url, json_request_args(body)) }
1864
+ end
1865
+
1866
+ def raw_post(url, body)
1867
+ wrapped_request { @auth.api_client.request(:post, url, json_request_args(body)) }
1868
+ end
1869
+
1870
+ # Availability Query could originally be authenticated via an access_token
1871
+ # Whilst it should be authed via an API key now, we try access_token first
1872
+ # for backward compatibility
1873
+ def availability_post(url, body)
1874
+ if @auth.access_token
1875
+ post(url, body)
1876
+ else
1877
+ wrapped_request { api_key!.post(url, json_request_args(body)) }
1878
+ end
1879
+ end
1880
+
1881
+ def wrapped_request
1882
+ yield
1883
+ rescue OAuth2::Error => e
1884
+ raise Errors.map_error(e)
1885
+ end
1886
+
1887
+ def parse_collection(type, attr, response)
1888
+ ResponseParser.new(response).parse_collection(type, attr)
1889
+ end
1890
+
1891
+ def parse_collections(response, mappings)
1892
+ ResponseParser.new(response).parse_collections(mappings)
1893
+ end
1894
+
1895
+ def parse_json(type, attr = nil, response)
1896
+ ResponseParser.new(response).parse_json(type, attr)
1897
+ end
1898
+
1899
+ def json_request_args(body_hash)
1900
+ if body_hash
1901
+ {
1902
+ body: JSON.generate(body_hash),
1903
+ headers: { "Content-Type" => "application/json; charset=utf-8" },
1904
+ }
1905
+ else
1906
+ {}
1907
+ end
1908
+ end
1909
+
1910
+ class PagedResultIterator
1911
+ include Enumerable
1912
+
1913
+ def initialize(page_parser, items_key, access_token, url, params)
1914
+ @page_parser = page_parser
1915
+ @items_key = items_key
1916
+ @access_token = access_token
1917
+ @url = url
1918
+ @params = params
1919
+ @first_page = get_page(url, params)
1920
+ end
1921
+
1922
+ def each
1923
+ page = @first_page
1924
+
1925
+ page[@items_key].each do |item|
1926
+ yield item
1927
+ end
1928
+
1929
+ while page.pages.next_page?
1930
+ page = get_page(page.pages.next_page)
1931
+
1932
+ page[@items_key].each do |item|
1933
+ yield item
1934
+ end
1935
+ end
1936
+ end
1937
+
1938
+ private
1939
+
1940
+ attr_reader :access_token
1941
+ attr_reader :params
1942
+ attr_reader :url
1943
+
1944
+ def get_page(url, params = {})
1945
+ response = http_get(url, params)
1946
+ parse_page(response)
1947
+ end
1948
+
1949
+ def http_get(url, params = {})
1950
+ response = Faraday.get(url, params, oauth_headers)
1951
+ Errors.raise_if_error(response)
1952
+ response
1953
+ end
1954
+
1955
+ def oauth_headers
1956
+ {
1957
+ "Authorization" => "Bearer #{access_token.token}",
1958
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
1959
+ }
1960
+ end
1961
+
1962
+ def parse_page(response)
1963
+ ResponseParser.new(response).parse_json(@page_parser)
1964
+ end
1965
+ end
1966
+
1967
+ def api_url
1968
+ ::Cronofy.api_url(@data_center)
1969
+ end
117
1970
  end
118
1971
 
119
- end
1972
+ # Deprecated: Alias for Client for backwards compatibility.
1973
+ class Cronofy < Client
1974
+ end
1975
+ end