cronofy 0.0.5 → 0.1.0

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