cronofy 0.0.5 → 0.37.7

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,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