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 +4 -4
- data/README.md +59 -31
- data/cronofy.gemspec +15 -12
- data/lib/cronofy.rb +1 -2
- data/lib/cronofy/auth.rb +26 -42
- data/lib/cronofy/client.rb +394 -74
- data/lib/cronofy/errors.rb +31 -10
- data/lib/cronofy/response_parser.rb +28 -3
- data/lib/cronofy/types.rb +143 -0
- data/lib/cronofy/version.rb +1 -1
- data/spec/lib/cronofy/auth_spec.rb +123 -0
- data/spec/lib/cronofy/client_spec.rb +519 -0
- data/spec/response_parser_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +55 -23
- data/.gitignore +0 -14
- data/.travis.yml +0 -16
- data/script/ci +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfa7415b5d284a6c099d6c2247430cb63a4e6241
|
4
|
+
data.tar.gz: 83c8cfe7ed6decaef9e78fce9c2eab83560434a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d86d15864c20933b9ed7e992e020d104163cbff810b8aded419eff2b4cc6f81b266af138b272dec5baa043d8dc5cefc976d4e04d11c94893bf450a104419b53
|
7
|
+
data.tar.gz: ae77c0ca26eec4bb3838ae014746f6ad90a0e018f07f4a8a6d349e818befa682d59556045e571195af21f469308a8b64d8cbcef704d057dcaa15ad36d30e069c
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
[](http://badge.fury.io/rb/cronofy)
|
5
5
|
[](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).
|
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
|
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
|
32
|
+
cronofy = Cronofy::Client.new
|
30
33
|
```
|
31
34
|
|
32
|
-
|
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
|
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
|
-
|
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
|
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
|
-
"
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
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',
|
99
|
+
event_id: 'uniq-id',
|
76
100
|
summary: 'Event summary',
|
77
101
|
description: 'Event description',
|
78
|
-
start: Time.now + 60 * 60 * 24,
|
79
|
-
end: Time.now + 60 * 60 * 25,
|
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
|
-
|
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,
|
117
|
+
cronofy.delete_event(calendar_id, 'uniq-id')
|
90
118
|
```
|
91
119
|
|
92
120
|
## Links
|
data/cronofy.gemspec
CHANGED
@@ -1,24 +1,27 @@
|
|
1
|
-
|
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.
|
21
|
-
spec.
|
22
|
-
spec.
|
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
|
data/lib/cronofy.rb
CHANGED
@@ -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
|
data/lib/cronofy/auth.rb
CHANGED
@@ -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
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@auth_client.auth_code.authorize_url(:
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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,
|
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
|
data/lib/cronofy/client.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
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
|
15
|
-
# see http://www.cronofy.com/developers/api#calendars
|
36
|
+
# Public: Lists all the calendars for the account.
|
16
37
|
#
|
17
|
-
#
|
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 =
|
20
|
-
|
50
|
+
response = get("/v1/calendars")
|
51
|
+
parse_collection(Calendar, "calendars", response)
|
21
52
|
end
|
22
53
|
|
23
|
-
# Public
|
24
|
-
#
|
25
|
-
#
|
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
|
-
#
|
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
|
-
#
|
35
|
-
|
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
|
-
|
41
|
-
|
42
|
-
}
|
106
|
+
body[:start] = to_iso8601(body[:start])
|
107
|
+
body[:end] = to_iso8601(body[:end])
|
43
108
|
|
44
|
-
|
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
|
49
|
-
|
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
|
-
#
|
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
|
-
|
182
|
+
delete("/v1/calendars/#{calendar_id}/events", event_id: event_id)
|
183
|
+
nil
|
184
|
+
end
|
57
185
|
|
58
|
-
|
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
|
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
|
-
#
|
66
|
-
#
|
211
|
+
# See http://www.cronofy.com/developers/api/alpha#list-channels for
|
212
|
+
# reference.
|
67
213
|
#
|
68
|
-
# Returns
|
69
|
-
|
70
|
-
|
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
|
74
|
-
#
|
228
|
+
# Public: Closes a notification channel.
|
229
|
+
#
|
230
|
+
# channel_id - The String Cronofy ID for the channel to close.
|
75
231
|
#
|
76
|
-
#
|
77
|
-
# redirect_uri - String URI returned to
|
232
|
+
# Returns nothing.
|
78
233
|
#
|
79
|
-
#
|
80
|
-
|
81
|
-
|
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
|
85
|
-
#
|
247
|
+
# Public: Retrieves the details of the account.
|
248
|
+
#
|
249
|
+
# See http://www.cronofy.com/developers/api#account for reference.
|
86
250
|
#
|
87
|
-
# Returns
|
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
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
436
|
+
# Deprecated: Alias for Client for backwards compatibility.
|
437
|
+
class Cronofy < Client
|
438
|
+
end
|
439
|
+
end
|