cronofy 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1be096b17b4eb2b3422337a2d168b7f27559493c
4
- data.tar.gz: e696a6175e17d0465912d7810342cfd1352b7191
3
+ metadata.gz: bfa7415b5d284a6c099d6c2247430cb63a4e6241
4
+ data.tar.gz: 83c8cfe7ed6decaef9e78fce9c2eab83560434a1
5
5
  SHA512:
6
- metadata.gz: 5ea577cd0076d2aa66cdf8c7ecd6791aa1d80ef216a51535d51a46b351b586ef7fcb4456ce8cb4be78c010f8bcf82f6b12239f82d2948131324c6ae746696f9b
7
- data.tar.gz: 0071814777c83e679d6b704595b756c6aa709c4540430fff18a2b29caefcbbeca8309390931259fd9b5c5159fb1e82c34920e2f9c65aebe11ee6490919223d74
6
+ metadata.gz: 9d86d15864c20933b9ed7e992e020d104163cbff810b8aded419eff2b4cc6f81b266af138b272dec5baa043d8dc5cefc976d4e04d11c94893bf450a104419b53
7
+ data.tar.gz: ae77c0ca26eec4bb3838ae014746f6ad90a0e018f07f4a8a6d349e818befa682d59556045e571195af21f469308a8b64d8cbcef704d057dcaa15ad36d30e069c
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/cronofy.svg)](http://badge.fury.io/rb/cronofy)
5
5
  [![Join the chat at https://gitter.im/cronofy/cronofy-ruby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cronofy/cronofy-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6
6
 
7
- [Cronofy](http://www.cronofy.com) - one API for all the calendars (Google, Outlook, iCloud, Exchange). This gem is an interface for easy use of [Cronofy API](http://www.cronofy.com/developers/api) with Ruby.
7
+ [Cronofy](http://www.cronofy.com) - one API for all the calendars (Google, Outlook, iCloud, Exchange).
8
8
 
9
9
  ## Installation
10
10
 
@@ -24,69 +24,97 @@ Or install it yourself as:
24
24
 
25
25
  ## Usage
26
26
 
27
- You have to register on cronofy website and create an application there. You will get a client id and client secret which you will have to pass to initializer. You can also pass a token and refresh token that you will get later, or leave it blank in case if you don't have it now:
27
+ You have to register on the Cronofy website and create an application there. You will then get a client id and client secret.
28
+
29
+ You can either set them as the enviroment variables `CRONOFY_CLIENT_ID` and `CRONOFY_CLIENT_SECRET` and have them picked up automatically when creating a new `Cronofy::Client`:
30
+
28
31
  ```ruby
29
- cronofy = Cronofy::Client.new('CLIENT_ID', 'CLIENT_SECRET', 'TOKEN', 'REFRESH_TOKEN')
32
+ cronofy = Cronofy::Client.new
30
33
  ```
31
34
 
32
- Generate a link for a user to grant access for his calendars:
35
+ Or you can specify them explicitly:
36
+
37
+ ```ruby
38
+ cronofy = Cronofy::Client.new(client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET')
39
+ ```
40
+
41
+ You can also pass an existing access token and refresh token if you already have a pair for the user:
42
+
43
+ ```ruby
44
+ cronofy = Cronofy::Client.new(access_token: 'ACCESS_TOKEN', refresh_token: 'REFRESH_TOKEN')
45
+ ```
46
+
47
+ ### Authorization
48
+
49
+ Generate a link for a user to grant access for their calendars:
50
+
33
51
  ```ruby
34
52
  cronofy.user_auth_link('http://localhost:3000/oauth2/callback')
35
53
  ```
36
54
 
37
- The specified url is a page on your website that will handle callback and get a code parameter out of it. On a callback you will have a param[:code] that you will need in order to get a token:
55
+ The returned URL is a page on your website that will handle the OAuth 2.0 callback and receive a code parameter. You can then use that code to retrieve an OAuth token granting access to the user's Cronofy account:
56
+
38
57
  ```ruby
39
58
  token = cronofy.get_token_from_code(code, 'http://localhost:3000/oauth2/callback')
40
59
  ```
41
- You can now save a token to pass it later to initializer.
60
+
61
+ You should save the `access_token` and `refresh_token` for later use.
62
+
63
+ ### List calendars
42
64
 
43
65
  Get a list of all the user calendars:
66
+
44
67
  ```ruby
45
68
  cronofy.list_calendars
46
69
  ```
47
70
 
48
- You will get a list of user's calendars in a json format. The example of such a response:
71
+ You will get a list of the user's calendars with each entry being a wrapped
72
+ version of the following JSON structure:
73
+
49
74
  ```json
50
75
  {
51
- "calendars":[
52
- {
53
- "provider_name":"google",
54
- "profile_name":"YYYYYYYY@gmail.com",
55
- "calendar_id":"cal_YYYYYYYY-UNIQUE_CAL_ID_HERE-YYYYYYYY",
56
- "calendar_name":"Office Calendar",
57
- "calendar_readonly":false,
58
- "calendar_deleted":false
59
- },
60
- {
61
- "provider_name":"google",
62
- "profile_name":"XXXXXXX@gmail.com",
63
- "calendar_id":"cal_XXXXXXXX-UNIQUE_CAL_ID_HERE-XXXXXXXXX",
64
- "calendar_name":"Home Calendar",
65
- "calendar_readonly":false,
66
- "calendar_deleted":false
67
- }
68
- ]
76
+ "provider_name": "google",
77
+ "profile_name": "YYYYYYYY@gmail.com",
78
+ "calendar_id": "cal_YYYYYYYY-UNIQUE_CAL_ID_HERE-YYYYYYYY",
79
+ "calendar_name": "Office Calendar",
80
+ "calendar_readonly": false,
81
+ "calendar_deleted": false
69
82
  }
70
83
  ```
71
84
 
72
- To create/update an event in user's calendar:
85
+ The properties can be accessed like so:
86
+
87
+ ```ruby
88
+ calendar = cronofy.list_calendars.first
89
+ calendar.calendar_id
90
+ # => "cal_YYYYYYYY-UNIQUE_CAL_ID_HERE-YYYYYYYY"
91
+ ```
92
+
93
+ ### Create or update events
94
+
95
+ To create/update an event in the user's calendar:
96
+
73
97
  ```ruby
74
98
  event_data = {
75
- event_id: 'uniq-id', # uniq id of event
99
+ event_id: 'uniq-id',
76
100
  summary: 'Event summary',
77
101
  description: 'Event description',
78
- start: Time.now + 60 * 60 * 24, # will be converted to .utc.iso8601 internally,
79
- end: Time.now + 60 * 60 * 25, # the same convertion here
102
+ start: Time.now + 60 * 60 * 24,
103
+ end: Time.now + 60 * 60 * 25,
80
104
  location: {
81
105
  description: "Meeting room"
82
106
  }
83
107
  }
84
- cronofy.upsert(calendar_id, event_data)
108
+
109
+ cronofy.upsert_event(calendar_id, event_data)
85
110
  ```
86
111
 
112
+ ### Delete events
113
+
87
114
  To delete an event from user's calendar:
115
+
88
116
  ```ruby
89
- cronofy.delete_event(calendar_id, event_id)
117
+ cronofy.delete_event(calendar_id, 'uniq-id')
90
118
  ```
91
119
 
92
120
  ## Links
@@ -1,24 +1,27 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'cronofy/version'
1
+ require File.expand_path('../lib/cronofy/version', __FILE__)
5
2
 
6
3
  Gem::Specification.new do |spec|
7
4
  spec.name = "cronofy"
8
5
  spec.version = Cronofy::VERSION
9
- spec.authors = ["Sergii Paryzhskyi"]
10
- spec.email = ["parizhskiy@gmail.com"]
6
+ spec.authors = ["Sergii Paryzhskyi", "Garry Shutler"]
7
+ spec.email = ["parizhskiy@gmail.com", "garry@cronofy.com"]
11
8
  spec.summary = %q{Cronofy - one API for all the calendars}
9
+ spec.description = %q{Ruby wrapper for Cronofy's unified calendar API}
12
10
  spec.homepage = "https://github.com/cronofy/cronofy-ruby"
13
11
  spec.license = "MIT"
14
12
 
15
- spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
13
  spec.require_paths = ["lib"]
19
14
 
20
- spec.add_development_dependency "bundler", "~> 1.7"
21
- spec.add_development_dependency "rake", "~> 10.0"
22
- spec.add_development_dependency "rspec", "~> 3.2"
15
+ spec.files = %w{Gemfile LICENSE.txt README.md Rakefile cronofy.gemspec}
16
+ spec.files += Dir['lib/**/*.rb']
17
+ spec.files += Dir['spec/**/*.rb']
18
+ spec.test_files = Dir['spec/**/*.rb']
19
+
23
20
  spec.add_runtime_dependency "oauth2", "~> 1.0"
21
+ spec.add_runtime_dependency "hashie", "~> 3.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.2"
26
+ spec.add_development_dependency "webmock", "~> 1.21"
24
27
  end
@@ -1,12 +1,12 @@
1
1
  require "cronofy/version"
2
2
  require "cronofy/errors"
3
+ require "cronofy/types"
3
4
  require "cronofy/auth"
4
5
  require "cronofy/client"
5
6
  require "cronofy/response_parser"
6
7
  require 'json'
7
8
 
8
9
  module Cronofy
9
-
10
10
  def self.api_url
11
11
  @api_url ||= (ENV['CRONOFY_API_URL'] || "https://api.cronofy.com")
12
12
  end
@@ -22,5 +22,4 @@ module Cronofy
22
22
  def self.app_url=(value)
23
23
  @app_url = value
24
24
  end
25
-
26
25
  end
@@ -1,36 +1,13 @@
1
1
  require "oauth2"
2
2
 
3
3
  module Cronofy
4
+ # Internal: Class for dealing with authentication and authorization issues.
4
5
  class Auth
5
- class Credentials
6
-
7
- attr_reader :access_token,
8
- :expires_at,
9
- :expires_in,
10
- :refresh_token
11
-
12
- def initialize(oauth_token)
13
- @access_token = oauth_token.token
14
- @expires_at = oauth_token.expires_at
15
- @expires_in = oauth_token.expires_in
16
- @refresh_token = oauth_token.refresh_token
17
- end
18
-
19
- def to_hash
20
- {
21
- access_token: access_token,
22
- refresh_token: refresh_token,
23
- expires_in: expires_in,
24
- expires_at: expires_at
25
- }
26
- end
27
- end
28
-
29
6
  attr_reader :access_token
30
7
 
31
- def initialize(client_id, client_secret, token=nil, refresh_token=nil)
32
- @auth_client = OAuth2::Client.new(client_id, client_secret, site: ::Cronofy.app_url)
33
- @api_client = OAuth2::Client.new(client_id, client_secret, site: ::Cronofy.api_url)
8
+ def initialize(client_id, client_secret, token = nil, refresh_token = nil)
9
+ @auth_client = OAuth2::Client.new(client_id, client_secret, site: ::Cronofy.app_url, connection_opts: { headers: { "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}" } })
10
+ @api_client = OAuth2::Client.new(client_id, client_secret, site: ::Cronofy.api_url, connection_opts: { headers: { "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}" } })
34
11
 
35
12
  set_access_token(token, refresh_token) if token
36
13
  end
@@ -39,28 +16,28 @@ module Cronofy
39
16
  #
40
17
  # redirect_uri String, the URI to return to after authorization
41
18
  # scope Array of String, the scope requested
42
- # Default: [read_account, list_calendars, read_events, create_event, delete_event]
43
- # see: http://www.cronofy.com/developers/api#authorization
44
19
  #
45
- # Returns String URL
46
- def user_auth_link(redirect_uri, scope=nil)
47
- scope ||= %w{read_account list_calendars read_events create_event delete_event}
48
-
49
- @auth_client.auth_code.authorize_url(:redirect_uri => redirect_uri, :response_type => 'code', :scope => scope.join(' '))
20
+ # See http://www.cronofy.com/developers/api#authorization for reference.
21
+ #
22
+ # Returns the URL as a String.
23
+ def user_auth_link(redirect_uri, scope)
24
+ @auth_client.auth_code.authorize_url(redirect_uri: redirect_uri, response_type: 'code', scope: scope.join(' '))
50
25
  end
51
26
 
52
27
  def get_token_from_code(code, redirect_uri)
53
- auth_token = @auth_client.auth_code.get_token(code, :redirect_uri => redirect_uri)
54
- set_access_token_from_auth_token(auth_token)
55
- Credentials.new(@access_token)
28
+ do_request do
29
+ @access_token = @auth_client.auth_code.get_token(code, redirect_uri: redirect_uri)
30
+ Credentials.new(@access_token)
31
+ end
56
32
  end
57
33
 
58
34
  # Public: Refreshes the access token
59
35
  # Returns Hash of token elements to allow client to update in local store for user
60
36
  def refresh!
61
- auth_token = access_token.refresh!
62
- set_access_token_from_auth_token(auth_token)
63
- Credentials.new(@access_token)
37
+ do_request do
38
+ @access_token = access_token.refresh!
39
+ Credentials.new(@access_token)
40
+ end
64
41
  end
65
42
 
66
43
  def set_access_token_from_auth_token(auth_token)
@@ -68,8 +45,15 @@ module Cronofy
68
45
  end
69
46
 
70
47
  def set_access_token(token, refresh_token)
71
- @access_token = OAuth2::AccessToken.new(@api_client, token, { refresh_token: refresh_token })
48
+ @access_token = OAuth2::AccessToken.new(@api_client, token, refresh_token: refresh_token)
72
49
  end
73
50
 
51
+ private
52
+
53
+ def do_request(&block)
54
+ block.call
55
+ rescue OAuth2::Error => e
56
+ raise Errors.map_error(e)
57
+ end
74
58
  end
75
- end
59
+ end
@@ -1,119 +1,439 @@
1
1
  module Cronofy
2
-
2
+ # Public: Primary class for interacting with the Cronofy API.
3
3
  class Client
4
+ # Public: The scope to request if none is explicitly specified by the
5
+ # caller.
6
+ DEFAULT_OAUTH_SCOPE = %w{
7
+ read_account
8
+ list_calendars
9
+ read_events
10
+ create_event
11
+ delete_event
12
+ }.freeze
4
13
 
5
- def initialize(client_id, client_secret, token=nil, refresh_token=nil)
6
- @auth = Auth.new(client_id, client_secret, token, refresh_token)
7
- end
14
+ # Public: Initialize a new Cronofy::Client.
15
+ #
16
+ # options - A Hash of options used to initialize the client (default: {}):
17
+ # :access_token - An existing access token String for the user's
18
+ # account (optional).
19
+ # :client_id - The client ID String of your Cronofy OAuth
20
+ # application (default:
21
+ # ENV["CRONOFY_CLIENT_ID"]).
22
+ # :client_secret - The client secret String of your Cronofy OAuth
23
+ # application (default:
24
+ # ENV["CRONOFY_CLIENT_SECRET"]).
25
+ # :refresh_token - An existing refresh token String for the user's
26
+ # account (optional).
27
+ def initialize(options = {})
28
+ access_token = options[:access_token]
29
+ client_id = options.fetch(:client_id, ENV["CRONOFY_CLIENT_ID"])
30
+ client_secret = options.fetch(:client_secret, ENV["CRONOFY_CLIENT_SECRET"])
31
+ refresh_token = options[:refresh_token]
8
32
 
9
- def access_token!
10
- raise CredentialsMissingError.new unless @auth.access_token
11
- @auth.access_token
33
+ @auth = Auth.new(client_id, client_secret, access_token, refresh_token)
12
34
  end
13
35
 
14
- # Public : Lists the calendars or the user across all of the calendar accounts
15
- # see http://www.cronofy.com/developers/api#calendars
36
+ # Public: Lists all the calendars for the account.
16
37
  #
17
- # Returns Hash of calendars
38
+ # See http://www.cronofy.com/developers/api#calendars for reference.
39
+ #
40
+ # Returns an Array of Calendars
41
+ #
42
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
43
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
44
+ # longer valid.
45
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
46
+ # include the required scope.
47
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
48
+ # limits for the application.
18
49
  def list_calendars
19
- response = do_request { access_token!.get("/v1/calendars") }
20
- ResponseParser.new(response).parse_json
50
+ response = get("/v1/calendars")
51
+ parse_collection(Calendar, "calendars", response)
21
52
  end
22
53
 
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
54
+ # Public: Creates or updates an event for the event_id in the calendar
55
+ # relating to the given calendar_id.
56
+ #
57
+ # calendar_id - The String Cronofy ID for the calendar to upsert the event
58
+ # to.
59
+ # event - A Hash describing the event with symbolized keys:
60
+ # :event_id - A String uniquely identifying the event for
61
+ # your application (note: this is NOT an ID
62
+ # generated by Cronofy).
63
+ # :summary - A String to use as the summary, sometimes
64
+ # referred to as the name or title, of the
65
+ # event.
66
+ # :description - A String to use as the description, sometimes
67
+ # referred to as the notes or body, of the
68
+ # event.
69
+ # :start - The Time the event starts.
70
+ # :end - The Time the event ends.
71
+ # :location - A Hash describing the location of the event
72
+ # with symbolized keys (optional):
73
+ # :description - A String describing the
74
+ # location.
75
+ #
76
+ # Examples
77
+ #
78
+ # client.upsert_event(
79
+ # "cal_n23kjnwrw2_jsdfjksn234",
80
+ # event_id: "qTtZdczOccgaPncGJaCiLg",
81
+ # summary: "Board meeting",
82
+ # description: "Discuss plans for the next quarter.",
83
+ # start: Time.utc(2014, 8, 5, 15, 30),
84
+ # end: Time.utc(2014, 8, 5, 17, 30),
85
+ # location: {
86
+ # description: "Board room"
87
+ # })
88
+ #
89
+ # See http://www.cronofy.com/developers/api#upsert-event for reference.
26
90
  #
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
91
+ # Returns nothing.
33
92
  #
34
- # Returns nothing
35
- def create_or_update_event(calendar_id, event)
93
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
94
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
95
+ # longer valid.
96
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
97
+ # include the required scope.
98
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
99
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
100
+ # parameters.
101
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
102
+ # limits for the application.
103
+ def upsert_event(calendar_id, event)
36
104
  body = event.dup
37
- body[:start] = event[:start].utc.iso8601
38
- body[:end] = event[:end].utc.iso8601
39
105
 
40
- headers = {
41
- 'Content-Type' => 'application/json'
42
- }
106
+ body[:start] = to_iso8601(body[:start])
107
+ body[:end] = to_iso8601(body[:end])
43
108
 
44
- do_request { access_token!.post("/v1/calendars/#{calendar_id}/events", { body: JSON.generate(body), headers: headers }) }
109
+ post("/v1/calendars/#{calendar_id}/events", body)
110
+ nil
45
111
  end
46
- alias_method :upsert_event, :create_or_update_event
47
112
 
48
- # Public : Deletes an event from the specified calendar
49
- # see http://www.cronofy.com/developers/api#delete-event
113
+ # Public: Alias for #upsert_event
114
+ alias_method :create_or_update_event, :upsert_event
115
+
116
+ # Public: Returns a lazily-evaluated Enumerable of Events that satisfy the
117
+ # given query criteria.
118
+ #
119
+ # options - The Hash options used to refine the selection (default: {}):
120
+ # :from - The minimum Date from which to return events
121
+ # (optional).
122
+ # :to - The Date to return events up until (optional).
123
+ # :tzid - A String representing a known time zone
124
+ # identifier from the IANA Time Zone Database
125
+ # (default: Etc/UTC).
126
+ # :include_deleted - A Boolean specifying whether events that have
127
+ # been deleted should included or excluded from
128
+ # the results (optional).
129
+ # :include_moved - A Boolean specifying whether events that have
130
+ # ever existed within the given window should
131
+ # be included or excluded from the results
132
+ # (optional).
133
+ # :last_modified - The Time that events must be modified on or
134
+ # after in order to be returned (optional).
135
+ #
136
+ # See http://www.cronofy.com/developers/api#read-events for reference.
137
+ #
138
+ # Returns a lazily-evaluated Enumerable of Events
139
+ #
140
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
141
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
142
+ # longer valid.
143
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
144
+ # include the required scope.
145
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
146
+ # parameters.
147
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
148
+ # limits for the application.
149
+ def read_events(options = {})
150
+ params = READ_EVENTS_DEFAULT_PARAMS.merge(options)
151
+
152
+ READ_EVENTS_TIME_PARAMS.select { |tp| params.key?(tp) }.each do |tp|
153
+ params[tp] = to_iso8601(params[tp])
154
+ end
155
+
156
+ url = ::Cronofy.api_url + "/v1/events"
157
+ ReadEventsIterator.new(access_token!, url, params)
158
+ end
159
+
160
+ # Public: Deletes an event from the specified calendar
161
+ #
162
+ # calendar_id - The String Cronofy ID for the calendar to delete the event
163
+ # from.
164
+ # event_id - A String uniquely identifying the event for your application
165
+ # (note: this is NOT an ID generated by Cronofy).
50
166
  #
51
- # calendar_id - String Cronofy ID for the calendar containing the event
52
- # event_id - String client ID for the event
167
+ # See http://www.cronofy.com/developers/api#delete-event for reference.
53
168
  #
54
- # Returns nothing
169
+ # Returns nothing.
170
+ #
171
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
172
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
173
+ # longer valid.
174
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
175
+ # include the required scope.
176
+ # Raises Cronofy::NotFoundError if the calendar does not exist.
177
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
178
+ # parameters.
179
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
180
+ # limits for the application.
55
181
  def delete_event(calendar_id, event_id)
56
- params = { event_id: event_id }
182
+ delete("/v1/calendars/#{calendar_id}/events", event_id: event_id)
183
+ nil
184
+ end
57
185
 
58
- do_request { access_token!.delete("/v1/calendars/#{calendar_id}/events", { params: params }) }
186
+ # Public: Creates a notification channel with a callback URL
187
+ #
188
+ # callback_url - A String specifing the callback URL for the channel.
189
+ #
190
+ # See http://www.cronofy.com/developers/api/alpha#create-channel for
191
+ # reference.
192
+ #
193
+ # Returns a Channel.
194
+ #
195
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
196
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
197
+ # longer valid.
198
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
199
+ # include the required scope.
200
+ # Raises Cronofy::InvalidRequestError if the request contains invalid
201
+ # parameters.
202
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
203
+ # limits for the application.
204
+ def create_channel(callback_url)
205
+ response = post("/v1/channels", callback_url: callback_url)
206
+ parse_json(Channel, "channel", response)
59
207
  end
60
208
 
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
209
+ # Public: Lists all the notification channels for the account.
64
210
  #
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)
211
+ # See http://www.cronofy.com/developers/api/alpha#list-channels for
212
+ # reference.
67
213
  #
68
- # Returns String
69
- def user_auth_link(redirect_uri, scope=nil)
70
- @auth.user_auth_link(redirect_uri, scope)
214
+ # Returns an Array of Channels.
215
+ #
216
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
217
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
218
+ # longer valid.
219
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
220
+ # include the required scope.
221
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
222
+ # limits for the application.
223
+ def list_channels
224
+ response = get("/v1/channels")
225
+ parse_collection(Channel, "channels", response)
71
226
  end
72
227
 
73
- # Public : Returns the access_token for a given code and redirect_uri pair
74
- # see http://www.cronofy.com/developers/api#token-issue
228
+ # Public: Closes a notification channel.
229
+ #
230
+ # channel_id - The String Cronofy ID for the channel to close.
75
231
  #
76
- # code - String code returned to redirect_uri after authorization
77
- # redirect_uri - String URI returned to
232
+ # Returns nothing.
78
233
  #
79
- # Returns Cronofy::Credentials
80
- def get_token_from_code(code, redirect_uri)
81
- @auth.get_token_from_code(code, redirect_uri)
234
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
235
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
236
+ # longer valid.
237
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
238
+ # include the required scope.
239
+ # Raises Cronofy::NotFoundError if the channel does not exist.
240
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
241
+ # limits for the application.
242
+ def close_channel(channel_id)
243
+ delete("/v1/channels/#{channel_id}")
244
+ nil
82
245
  end
83
246
 
84
- # Public : Refreshes the access_token and periodically the refresh_token for authorization
85
- # see http://www.cronofy.com/developers/api#token-refresh
247
+ # Public: Retrieves the details of the account.
248
+ #
249
+ # See http://www.cronofy.com/developers/api#account for reference.
86
250
  #
87
- # Returns Cronofy::Credentials
251
+ # Returns an Account.
252
+ #
253
+ # Raises Cronofy::CredentialsMissingError if no credentials available.
254
+ # Raises Cronofy::AuthenticationFailureError if the access token is no
255
+ # longer valid.
256
+ # Raises Cronofy::AuthorizationFailureError if the access token does not
257
+ # include the required scope.
258
+ # Raises Cronofy::TooManyRequestsError if the request exceeds the rate
259
+ # limits for the application.
260
+ def account
261
+ response = get("/v1/account")
262
+ parse_json(Account, "account", response)
263
+ end
264
+
265
+ # Public: Generates a URL to send the user to in order to perform the OAuth
266
+ # 2.0 authorization process.
267
+ #
268
+ # redirect_url - A String specifing the URL to return the user to once they
269
+ # have completed the authorization steps.
270
+ # scope - Array of scopes describing the access to request from the
271
+ # user to the users calendars (default:
272
+ # DEFAULT_OAUTH_SCOPE).
273
+ #
274
+ # See http://www.cronofy.com/developers/api#authorization for reference.
275
+ #
276
+ # Returns the URL as a String.
277
+ def user_auth_link(redirect_url, scope = DEFAULT_OAUTH_SCOPE)
278
+ @auth.user_auth_link(redirect_url, scope)
279
+ end
280
+
281
+ # Public: Retrieves the OAuth credentials authorized for the given code and
282
+ # redirect URL pair.
283
+ #
284
+ # code - String code returned to redirect_url after authorization.
285
+ # redirect_url - A String specifing the URL the user returned to once they
286
+ # had completed the authorization steps.
287
+ #
288
+ # See http://www.cronofy.com/developers/api#token-issue for reference.
289
+ #
290
+ # Returns a set of Cronofy::Credentials for the account.
291
+ #
292
+ # Raises Cronofy::BadRequestError if the code is unknown, has been revoked,
293
+ # or the code and redirect URL do not match.
294
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
295
+ # not valid.
296
+ def get_token_from_code(code, redirect_url)
297
+ @auth.get_token_from_code(code, redirect_url)
298
+ end
299
+
300
+ # Public: Refreshes the credentials for the account's access token.
301
+ #
302
+ # Usually called in response to a Cronofy::AuthenticationFailureError as
303
+ # these usually occur when the access token has expired and needs
304
+ # refreshing.
305
+ #
306
+ # See http://www.cronofy.com/developers/api#token-refresh for reference.
307
+ #
308
+ # Returns a set of Cronofy::Credentials for the account.
309
+ #
310
+ # Raises Cronofy::BadRequestError if refresh token code is unknown or has
311
+ # been revoked.
312
+ # Raises Cronofy::AuthenticationFailureError if the client ID and secret are
313
+ # not valid.
88
314
  def refresh_access_token
89
315
  @auth.refresh!
90
316
  end
91
317
 
92
- private
318
+ private
319
+
320
+ READ_EVENTS_DEFAULT_PARAMS = { tzid: "Etc/UTC" }.freeze
321
+
322
+ READ_EVENTS_TIME_PARAMS = %i{
323
+ from
324
+ to
325
+ last_modified
326
+ }.freeze
93
327
 
94
- ERROR_MAP = {
95
- 401 => ::Cronofy::AuthenticationFailureError,
96
- 403 => ::Cronofy::AuthorizationFailureError,
97
- 404 => ::Cronofy::NotFoundError,
98
- 422 => ::Cronofy::InvalidRequestError,
99
- 429 => ::Cronofy::TooManyRequestsError
100
- }
328
+ def access_token!
329
+ raise CredentialsMissingError.new unless @auth.access_token
330
+ @auth.access_token
331
+ end
332
+
333
+ def get(url, opts = {})
334
+ wrapped_request { access_token!.get(url, opts) }
335
+ end
101
336
 
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)
337
+ def post(url, body)
338
+ wrapped_request { access_token!.post(url, json_request_args(body)) }
339
+ end
340
+
341
+ def delete(url, body = nil)
342
+ wrapped_request { access_token!.delete(url, json_request_args(body)) }
343
+ end
344
+
345
+ def wrapped_request
346
+ yield
347
+ rescue OAuth2::Error => e
348
+ raise Errors.map_error(e)
349
+ end
350
+
351
+ def parse_collection(type, attr, response)
352
+ ResponseParser.new(response).parse_collection(type, attr)
353
+ end
354
+
355
+ def parse_json(type, attr = nil, response)
356
+ ResponseParser.new(response).parse_json(type, attr)
357
+ end
358
+
359
+ def json_request_args(body_hash)
360
+ if body_hash
361
+ {
362
+ body: JSON.generate(body_hash),
363
+ headers: { "Content-Type" => "application/json; charset=utf-8" },
364
+ }
365
+ else
366
+ {}
108
367
  end
109
368
  end
110
369
 
111
- end
370
+ def to_iso8601(value)
371
+ case value
372
+ when NilClass
373
+ nil
374
+ when Time
375
+ value.getutc.iso8601
376
+ else
377
+ value.iso8601
378
+ end
379
+ end
112
380
 
113
- # Alias for backwards compatibility
114
- # depcrectated will be removed
115
- class Cronofy < Client
381
+ class ReadEventsIterator
382
+ include Enumerable
383
+
384
+ def initialize(access_token, url, params)
385
+ @access_token = access_token
386
+ @url = url
387
+ @params = params
388
+ end
389
+
390
+ def each
391
+ page = get_page(url, params)
392
+
393
+ page.events.each do |event|
394
+ yield event
395
+ end
396
+
397
+ while page.pages.next_page?
398
+ page = get_page(page.pages.next_page)
399
+
400
+ page.events.each do |event|
401
+ yield event
402
+ end
403
+ end
404
+ end
405
+
406
+ private
116
407
 
408
+ attr_reader :access_token
409
+ attr_reader :params
410
+ attr_reader :url
411
+
412
+ def get_page(url, params = {})
413
+ response = http_get(url, params)
414
+ parse_page(response)
415
+ end
416
+
417
+ def http_get(url, params = {})
418
+ response = Faraday.get(url, params, oauth_headers)
419
+ Errors.raise_if_error(response)
420
+ response
421
+ end
422
+
423
+ def oauth_headers
424
+ {
425
+ "Authorization" => "Bearer #{access_token.token}",
426
+ "User-Agent" => "Cronofy Ruby #{::Cronofy::VERSION}",
427
+ }
428
+ end
429
+
430
+ def parse_page(response)
431
+ ResponseParser.new(response).parse_json(PagedEventsResult)
432
+ end
433
+ end
117
434
  end
118
435
 
119
- end
436
+ # Deprecated: Alias for Client for backwards compatibility.
437
+ class Cronofy < Client
438
+ end
439
+ end