proctorserv-api 1.0.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.
- data/LICENSE +13 -0
- data/README.md +47 -0
- data/lib/proctorserv_api/exceptions/invalid_client_exception.rb +27 -0
- data/lib/proctorserv_api/exceptions/invalid_ip_exception.rb +27 -0
- data/lib/proctorserv_api/exceptions/invalid_signature_exception.rb +27 -0
- data/lib/proctorserv_api/exceptions/missing_required_parameter_exception.rb +27 -0
- data/lib/proctorserv_api/hashed_authenticator.rb +39 -0
- data/lib/proctorserv_api/rest_request_client.rb +48 -0
- data/lib/proctorserv_api/version.rb +7 -0
- data/lib/proctorserv_api.rb +183 -0
- data/spec/proctorserv_api_spec.rb +139 -0
- metadata +91 -0
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2013 ProctorCam, Inc.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
### ProctorservApi Client
|
2
|
+
|
3
|
+
A ruby wrapper around the Proctorserve scheduling API. Supports listing timeslots that are available to schedule a test session for, scheduling a test session, and canceling a test session.
|
4
|
+
|
5
|
+
#### Installation
|
6
|
+
|
7
|
+
$ gem install proctorserv-api
|
8
|
+
|
9
|
+
#### Configuration
|
10
|
+
|
11
|
+
The constructor takes two important options: the customer_identifier and shared_secret
|
12
|
+
|
13
|
+
_What's my customer_identifier?_
|
14
|
+
|
15
|
+
the Proctorserve service requires that incoming requests identify who is sending them with a `customer_id` parameter. Once your account has been set up, your customer id should be viewable and editable through the web interface.
|
16
|
+
|
17
|
+
_What's the shared_secret for?_
|
18
|
+
|
19
|
+
Proctorserve uses a secret key that is shared between customer and service as part of a two-factor authentication scheme. This is also viewable and editable through the web interface. It is important to keep this secure, otherwise a man in the middle can make requests on your behalf.
|
20
|
+
|
21
|
+
#### Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
proctorserv_api = Proctorcam::Proctorserv::ProctorservApi.new("our_customer_identifier", "our_shared_secret")
|
25
|
+
options = {
|
26
|
+
:scheduling_window_start => Time.now,
|
27
|
+
:scheduling_window_end => scheduling_window_start + 24 * 60 * 60, # tomorrow at this time
|
28
|
+
:test_length => 60 # minutes
|
29
|
+
}
|
30
|
+
available_timeslots = proctorserv_api.get_available_timeslots_between options
|
31
|
+
# returns a list of available timeslots between now and 24 hours from now.
|
32
|
+
```
|
33
|
+
|
34
|
+
#### Interface
|
35
|
+
|
36
|
+
The following methods are supported
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
proctorserv_api = Proctorcam::Proctorserv::ProctorservApi.new("our_customer_identifier", "our_shared_secret")
|
40
|
+
proctorserv_api.get_available_timeslots_between
|
41
|
+
proctorserv_api.get_available_timeslots_around
|
42
|
+
proctorserv_api.make_reservation
|
43
|
+
proctorserv_api.make_immediate_reservation
|
44
|
+
proctorserv_api.cancel_reservation
|
45
|
+
```
|
46
|
+
|
47
|
+
Further documentation for these methods can be found in the `doc/` folder
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# invalid_client_exception.rb
|
3
|
+
# ProctorservApi
|
4
|
+
#
|
5
|
+
# Copyright 2013 ProctorCam, Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# Created by Max Lahey on 6/6/13.
|
20
|
+
#
|
21
|
+
|
22
|
+
module ProctorCam
|
23
|
+
module Proctorserv
|
24
|
+
class InvalidClientException < Exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# invalid_ip_exception.rb
|
3
|
+
# ProctorservApi
|
4
|
+
#
|
5
|
+
# Copyright 2013 ProctorCam, Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# Created by Max Lahey on 6/6/13.
|
20
|
+
#
|
21
|
+
|
22
|
+
module ProctorCam
|
23
|
+
module Proctorserv
|
24
|
+
class InvalidIpException < Exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# invalid_signature_exception.rb
|
3
|
+
# ProctorservApi
|
4
|
+
#
|
5
|
+
# Copyright 2013 ProctorCam, Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# Created by Max Lahey on 6/6/13.
|
20
|
+
#
|
21
|
+
|
22
|
+
module ProctorCam
|
23
|
+
module Proctorserv
|
24
|
+
class InvalidSignatureException < Exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# missing_required_parameter_exception.rb
|
3
|
+
# ProctorservApi
|
4
|
+
#
|
5
|
+
# Copyright 2013 ProctorCam, Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
# Created by Max Lahey on 6/6/13.
|
20
|
+
#
|
21
|
+
|
22
|
+
module ProctorCam
|
23
|
+
module Proctorserv
|
24
|
+
class MissingRequiredParameterException < Exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
#
|
5
|
+
# hashed_authenticator.rb
|
6
|
+
# ProctorservApi
|
7
|
+
#
|
8
|
+
# Copyright 2013 ProctorCam, Inc
|
9
|
+
#
|
10
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
11
|
+
# you may not use this file except in compliance with the License.
|
12
|
+
# You may obtain a copy of the License at
|
13
|
+
#
|
14
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
15
|
+
#
|
16
|
+
# Unless required by applicable law or agreed to in writing, software
|
17
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
18
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
19
|
+
# See the License for the specific language governing permissions and
|
20
|
+
# limitations under the License.
|
21
|
+
#
|
22
|
+
# Created by Max Lahey on 6/6/13.
|
23
|
+
#
|
24
|
+
|
25
|
+
module ProctorCam
|
26
|
+
module Proctorserv
|
27
|
+
class HashedAuthenticator
|
28
|
+
|
29
|
+
def self.apply_reverse_guid_and_sign(params, customer_identifier, shared_secret)
|
30
|
+
params[:guid] = SecureRandom.hex(32)
|
31
|
+
params[:customer_id] = customer_identifier
|
32
|
+
query_string = params.map{|k,v| "#{k}=#{v}" }.join('&') + shared_secret
|
33
|
+
params[:guid].reverse!
|
34
|
+
params[:signature] = Digest::SHA256.hexdigest query_string
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
require 'proctorserv_api/hashed_authenticator'
|
4
|
+
|
5
|
+
#
|
6
|
+
# rest_request_client.rb
|
7
|
+
# ProctorservApi
|
8
|
+
#
|
9
|
+
# Copyright 2013 ProctorCam, Inc
|
10
|
+
#
|
11
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
12
|
+
# you may not use this file except in compliance with the License.
|
13
|
+
# You may obtain a copy of the License at
|
14
|
+
#
|
15
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
16
|
+
#
|
17
|
+
# Unless required by applicable law or agreed to in writing, software
|
18
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
19
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
20
|
+
# See the License for the specific language governing permissions and
|
21
|
+
# limitations under the License.
|
22
|
+
#
|
23
|
+
# Created by Max Lahey on 6/6/13.
|
24
|
+
#
|
25
|
+
|
26
|
+
module ProctorCam
|
27
|
+
module Proctorserv
|
28
|
+
class RestRequestClient
|
29
|
+
attr_accessor :response
|
30
|
+
|
31
|
+
def make_get_request(url, customer_identifier, shared_secret, params)
|
32
|
+
HashedAuthenticator.apply_reverse_guid_and_sign(params, customer_identifier, shared_secret)
|
33
|
+
RestClient.get url, :params => params, :accept => :json, :content_type => :json do |response, request, result, &block|
|
34
|
+
return response
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_post_request(url, customer_identifier, shared_secret, params)
|
39
|
+
HashedAuthenticator.apply_reverse_guid_and_sign(params, customer_identifier, shared_secret)
|
40
|
+
RestClient.post url, params.to_json, :accept => :json, :content_type => :json do |response, request, result, &block|
|
41
|
+
return response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'proctorserv_api/rest_request_client'
|
2
|
+
require 'proctorserv_api/exceptions/invalid_client_exception'
|
3
|
+
require 'proctorserv_api/exceptions/invalid_ip_exception'
|
4
|
+
require 'proctorserv_api/exceptions/invalid_signature_exception'
|
5
|
+
require 'proctorserv_api/exceptions/missing_required_parameter_exception'
|
6
|
+
|
7
|
+
#
|
8
|
+
# proctorserv_api.rb
|
9
|
+
# ProctorservApi
|
10
|
+
#
|
11
|
+
# Copyright 2013 ProctorCam, Inc
|
12
|
+
#
|
13
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
14
|
+
# you may not use this file except in compliance with the License.
|
15
|
+
# You may obtain a copy of the License at
|
16
|
+
#
|
17
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
18
|
+
#
|
19
|
+
# Unless required by applicable law or agreed to in writing, software
|
20
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
21
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
22
|
+
# See the License for the specific language governing permissions and
|
23
|
+
# limitations under the License.
|
24
|
+
#
|
25
|
+
# Created by Max Lahey on 6/6/13.
|
26
|
+
#
|
27
|
+
|
28
|
+
module ProctorCam
|
29
|
+
module Proctorserv
|
30
|
+
class ProctorservApi
|
31
|
+
|
32
|
+
def initialize(api_identifier, shared_secret, service_protocol = "https", service_url = "service.proctorcam.com")
|
33
|
+
@service_protocol = service_protocol
|
34
|
+
@service_url = service_url
|
35
|
+
@api_identifier = api_identifier
|
36
|
+
@shared_secret = shared_secret
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a list of schedulable timeslots (represented as Time objects) between two
|
40
|
+
# times for a given test that needs to be scheduled. This API request takes the
|
41
|
+
# provided session_duration into account when comparing Proctorserve business hours.
|
42
|
+
# The timeslots returned are specific for a test of duration provided.
|
43
|
+
#
|
44
|
+
# This method will raise exceptions related to authentication (InvalidClientException, InvalidSignatureException, InvalidIpException) or not providing required data (MissingRequiredParameterException)
|
45
|
+
#
|
46
|
+
# @param [Hash{Symbol => String}] options
|
47
|
+
# - :lower_bound (Time or int seconds since epoch) - the earlier of two timestamps to find available timeslots between
|
48
|
+
# - :upper_bound (Time or int seconds since epoch) - the later of two timestamps to find available timeslots between
|
49
|
+
# - :session_duration (int) - is the length in minutes alloted for this examination
|
50
|
+
# @return [Array<Time>] list of Time objects that represent schedulable timeslots in Proctorserve for a test of length session_duration between lower_bound and upper_bound
|
51
|
+
def get_available_timeslots_between(options)
|
52
|
+
requires_of options, [:lower_bound, :upper_bound, :session_duration]
|
53
|
+
url = "#{@service_protocol}://#{@service_url}/#{__method__}"
|
54
|
+
convert_times_to_integer options, :lower_bound, :upper_bound
|
55
|
+
response = RestRequestClient.new.make_get_request url, @api_identifier, @shared_secret, options
|
56
|
+
parsed_response = JSON.parse response
|
57
|
+
raise_exception_if_necessary(parsed_response) if response.code != 200
|
58
|
+
|
59
|
+
parsed_response.map{|timestamp| Time.at(timestamp)}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get a list of schedulable timeslots (represented as Time objects) around the
|
63
|
+
# time for a given test that needs to be scheduled. This API request takes the
|
64
|
+
# provided session_duration into account when comparing Proctorserve business hours.
|
65
|
+
# The timeslots returned are specific for a test of duration provided. At most
|
66
|
+
# 20 slots will be returned
|
67
|
+
#
|
68
|
+
# This method will raise exceptions related to authentication (InvalidClientException, InvalidSignatureException, InvalidIpException) or not providing required data (MissingRequiredParameterException)
|
69
|
+
#
|
70
|
+
# @param [Hash{Symbol => String}] options
|
71
|
+
# - :time (Time or int seconds since epoch) - time around which to find slots
|
72
|
+
# - :num_slots (int) - number of slots to return
|
73
|
+
# - :session_duration (int) - length in minutes alloted for this examination
|
74
|
+
# @return [Array<Time>] list (length num_slots or 20) of Time objects that represent schedulable timeslots in Proctorserve for a test of length session_duration around time passed in options hash
|
75
|
+
def get_available_timeslots_around(options)
|
76
|
+
requires_of options, [:time, :num_slots, :session_duration]
|
77
|
+
url = "#{@service_protocol}://#{@service_url}/#{__method__}"
|
78
|
+
convert_times_to_integer options, :time
|
79
|
+
response = RestRequestClient.new.make_get_request url, @api_identifier, @shared_secret, options
|
80
|
+
parsed_response = JSON.parse response
|
81
|
+
raise_exception_if_necessary(parsed_response) if response.code != 200
|
82
|
+
|
83
|
+
parsed_response.map{|timestamp| Time.at(timestamp)}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Makes a reservation for a session if the time passed in options is a schedulable time.
|
87
|
+
#
|
88
|
+
# This method will raise exceptions related to authentication (InvalidClientException, InvalidSignatureException, InvalidIpException) or not providing required data (MissingRequiredParameterException)
|
89
|
+
#
|
90
|
+
# @param [Hash{Symbol => String}] options
|
91
|
+
# - :time (Time or int seconds since epoch) - Scheduable time for examination, as obtained by get_available_timeslots_between or get_available_timeslots_around
|
92
|
+
# - :customer_subject_id (String) - Unique identifier in API customer's system for the person taking this test
|
93
|
+
# - :customer_client_subject_id (String) - Unique identifier in API customer's client's system for the person taking this test
|
94
|
+
# - :client_code (String) - Unique identifier for API customer's client
|
95
|
+
# - :reservation_id (String) - Unique identifier representing this test instance in API customer's system
|
96
|
+
# - :session_duration (int) - Session length in minutes
|
97
|
+
# - :exam_code (String) - Unique identifier representing the group of tests this specific test instance belongs to
|
98
|
+
# @return [int] session_id - the Proctorserve id for the created session. Will return nil if the time passed is not scheduable.
|
99
|
+
def make_reservation(options)
|
100
|
+
requires_of options, [:time, :customer_subject_id, :customer_client_subject_id, :client_code, :reservation_id, :session_duration, :exam_code]
|
101
|
+
url = "#{@service_protocol}://#{@service_url}/#{__method__}"
|
102
|
+
convert_times_to_integer options, :time
|
103
|
+
response = RestRequestClient.new.make_post_request url, @api_identifier, @shared_secret, options
|
104
|
+
|
105
|
+
parsed_response = JSON.parse response
|
106
|
+
raise_exception_if_necessary(parsed_response) if response.code != 200
|
107
|
+
|
108
|
+
return nil if response.code == 404
|
109
|
+
|
110
|
+
parsed_response["session_id"]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Makes a reservation for a session if now is a schedulable time.
|
114
|
+
#
|
115
|
+
# This method will raise exceptions related to authentication (InvalidClientException, InvalidSignatureException, InvalidIpException) or not providing required data (MissingRequiredParameterException)
|
116
|
+
#
|
117
|
+
# @param [Hash{Symbol => String}] options
|
118
|
+
# - :customer_subject_id (String) - Unique identifier in API customer's system for the person taking this test
|
119
|
+
# - :customer_client_subject_id (String) - Unique identifier in API customer's client's system for the person taking this test
|
120
|
+
# - :client_code (String) - Unique identifier for API customer's client
|
121
|
+
# - :reservation_id (String) - Unique identifier representing this test instance in API customer's system
|
122
|
+
# - :session_duration (int) - Session length in minutes
|
123
|
+
# - :exam_code (String) - Unique identifier representing the group of tests this specific test instance belongs to
|
124
|
+
# @return [int] session_id - the Proctorserve id for the created session. Will return nil if the time passed is not scheduable.
|
125
|
+
def make_immediate_reservation(options)
|
126
|
+
requires_of options, [:customer_subject_id, :customer_client_subject_id, :client_code, :reservation_id, :session_duration, :exam_code]
|
127
|
+
url = "#{@service_protocol}://#{@service_url}/#{__method__}"
|
128
|
+
convert_times_to_integer options, :time
|
129
|
+
response = RestRequestClient.new.make_post_request url, @api_identifier, @shared_secret, options
|
130
|
+
|
131
|
+
parsed_response = JSON.parse response
|
132
|
+
raise_exception_if_necessary(parsed_response) if response.code != 200
|
133
|
+
|
134
|
+
return nil if response.code == 404
|
135
|
+
|
136
|
+
parsed_response["session_id"]
|
137
|
+
end
|
138
|
+
|
139
|
+
# Cancels a reservation for a specific session_id. A session is no longer cancelable if it has already begun or the scheduled time has passed.
|
140
|
+
#
|
141
|
+
# This method will raise exceptions related to authentication (InvalidClientException, InvalidSignatureException, InvalidIpException) or not providing required data (MissingRequiredParameterException)
|
142
|
+
#
|
143
|
+
# @param [int] session_id Proctorserve id for the session to be canceled
|
144
|
+
# @return [Boolean] whether or not the test was successfully canceled.
|
145
|
+
def cancel_reservation(options)
|
146
|
+
requires_of options, [:session_id]
|
147
|
+
url = "#{@service_protocol}://#{@service_url}/#{__method__}"
|
148
|
+
response = RestRequestClient.new.make_post_request url, @api_identifier, @shared_secret, options
|
149
|
+
|
150
|
+
return true if response.code == 204
|
151
|
+
|
152
|
+
if response.code != 200
|
153
|
+
parsed_response = JSON.parse response
|
154
|
+
raise_exception_if_necessary(parsed_response)
|
155
|
+
end
|
156
|
+
|
157
|
+
false
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
|
162
|
+
def raise_exception_if_necessary(response)
|
163
|
+
case response["message"]
|
164
|
+
when "Not a valid client"
|
165
|
+
raise InvalidClientException, "The api_identifier for this instance of ProctorservApi does not match up with any for the service."
|
166
|
+
when "Signature hash is invalid"
|
167
|
+
raise InvalidSignatureException, "Authentication failed for the ProctorservApi request. It is most likely that the shared_secret does not match up with the Proctorserve record for your API client."
|
168
|
+
when "Request coming from invalid IP"
|
169
|
+
raise InvalidIpException, "Authentication failed for the ProctorservApi request because the IP address of this machine is not whitelisted for this API client in Proctorserve."
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def requires_of(hash, arguments)
|
174
|
+
arguments.each{|argument| raise MissingRequiredParameterException, "#{argument} is a required parameter" unless hash.key? argument}
|
175
|
+
end
|
176
|
+
|
177
|
+
def convert_times_to_integer(hash, *times)
|
178
|
+
times.each{|time| hash[time] = hash[time].to_i}
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require './lib/proctorserv_api'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
#
|
5
|
+
# proctorserv_api_spec.rb
|
6
|
+
# ProctorservApi
|
7
|
+
#
|
8
|
+
# Copyright 2013 ProctorCam, Inc
|
9
|
+
#
|
10
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
11
|
+
# you may not use this file except in compliance with the License.
|
12
|
+
# You may obtain a copy of the License at
|
13
|
+
#
|
14
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
15
|
+
#
|
16
|
+
# Unless required by applicable law or agreed to in writing, software
|
17
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
18
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
19
|
+
# See the License for the specific language governing permissions and
|
20
|
+
# limitations under the License.
|
21
|
+
#
|
22
|
+
# Created by Max Lahey on 6/6/13.
|
23
|
+
#
|
24
|
+
|
25
|
+
describe ProctorCam::Proctorserv::ProctorservApi do
|
26
|
+
|
27
|
+
before :all do
|
28
|
+
@proctorserv_api = ProctorCam::Proctorserv::ProctorservApi.new("api_identifier", "shared_secret", "http", "localhost:3000/api/scheduling")
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "when passed improper credentials" do
|
32
|
+
|
33
|
+
it "raises an invalid_client_exception when api_identifier is wrong" do
|
34
|
+
proctorserv_api = ProctorCam::Proctorserv::ProctorservApi.new("wrong", "shared_secret", "http", "localhost:3000/api/scheduling")
|
35
|
+
expect{proctorserv_api.get_available_timeslots_between({:lower_bound => Time.now.to_i, :upper_bound => Time.now.to_i + 24*60*60, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::InvalidClientException
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises an invalid_signature_exception when shared_secret is wrong" do
|
39
|
+
proctorserv_api = ProctorCam::Proctorserv::ProctorservApi.new("api_identifier", "wrong", "http", "localhost:3000/api/scheduling")
|
40
|
+
expect{proctorserv_api.get_available_timeslots_between({:lower_bound => Time.now.to_i, :upper_bound => Time.now.to_i + 24*60*60, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::InvalidSignatureException
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "get_available_timeslots_between" do
|
46
|
+
|
47
|
+
it "raises missing_required_parameter_exception when not passed lower_bound, upper_bound, and session_duration in options hash" do
|
48
|
+
expect{@proctorserv_api.get_available_timeslots_between({:lower_bound => Time.now.to_i, :upper_bound => Time.now.to_i + 24*60*60})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
49
|
+
expect{@proctorserv_api.get_available_timeslots_between({:lower_bound => Time.now.to_i, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
50
|
+
expect{@proctorserv_api.get_available_timeslots_between({:upper_bound => Time.now.to_i + 24*60*60, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns a list of schedulable timeslots when passed required parameters" do
|
54
|
+
response = @proctorserv_api.get_available_timeslots_between({:lower_bound => Time.now.to_i, :upper_bound => Time.now.to_i + 24*60*60, :session_duration => 60})
|
55
|
+
response.class.should == Array
|
56
|
+
response.length.should > 0
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "get_available_timeslots_around" do
|
62
|
+
|
63
|
+
it "raises missing_required_parameter_exception when not passed time, num_slots, and session_duration in options hash" do
|
64
|
+
expect{@proctorserv_api.get_available_timeslots_around({:time => Time.now.to_i, :num_slots => 10})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
65
|
+
expect{@proctorserv_api.get_available_timeslots_around({:time => Time.now.to_i, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
66
|
+
expect{@proctorserv_api.get_available_timeslots_around({:num_slots => 10, :session_duration => 60})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns a list of schedulable timeslots when passed required parameters" do
|
70
|
+
response = @proctorserv_api.get_available_timeslots_around({:time => Time.now.to_i, :num_slots => 10, :session_duration => 60})
|
71
|
+
response.class.should == Array
|
72
|
+
response.length.should == 10
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "make_reservation" do
|
78
|
+
|
79
|
+
it "raises missing_required_parameter_exception when not passed [:time, :customer_subject_id, :customer_client_subject_id, :client_code, :reservation_id, :session_duration, :exam_code] in options hash" do
|
80
|
+
options = {:time => Time.now.to_i, :customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
81
|
+
options.each do |k,v|
|
82
|
+
expect{@proctorserv_api.make_reservation options.reject{|key,v| key == k} }.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns nil when time provided is not schedulable" do
|
87
|
+
options = {:time => Time.new(1970, 1, 1, 6, 0, 0), :customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
88
|
+
response = @proctorserv_api.make_reservation options
|
89
|
+
response.should == nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns an int value representing session_id when provided a schedulable time and all required parameters" do
|
93
|
+
options = {:time => Time.now.to_i, :customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
94
|
+
response = @proctorserv_api.make_reservation options
|
95
|
+
response.class.should == Fixnum
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "make_immediate_reservation" do
|
101
|
+
|
102
|
+
it "raises missing_required_parameter_exception when not passed [:customer_subject_id, :customer_client_subject_id, :client_code, :reservation_id, :session_duration, :exam_code] in options hash" do
|
103
|
+
options = {:customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
104
|
+
options.each do |k,v|
|
105
|
+
expect{@proctorserv_api.make_immediate_reservation options.reject{|key,v| key == k} }.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns an int value representing session_id when now is a schedulable time and all required parameters" do
|
110
|
+
options = {:customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
111
|
+
response = @proctorserv_api.make_immediate_reservation options
|
112
|
+
response.class.should == Fixnum
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "cancel_reservation" do
|
118
|
+
|
119
|
+
it "raises missing_required_parameter_exception when not passed session_id in options hash" do
|
120
|
+
expect{@proctorserv_api.cancel_reservation({})}.to raise_error ProctorCam::Proctorserv::MissingRequiredParameterException
|
121
|
+
end
|
122
|
+
|
123
|
+
it "returns true when passed a session_id that that this client owns that is still cancelable" do
|
124
|
+
options = {:time => Time.now.to_i + 24*60*60, :customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
125
|
+
session_id = @proctorserv_api.make_reservation options
|
126
|
+
response = @proctorserv_api.cancel_reservation({:session_id => session_id})
|
127
|
+
response.should == true
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns false when passed a session_id that that this client owns that is no longer cancelable" do
|
131
|
+
options = {:time => Time.now.to_i - 5, :customer_subject_id => 'c_sub_1', :customer_client_subject_id => 'c_c_sub_1', :client_code => 'c_code', :reservation_id => 'res_id', :session_duration => 60, :exam_code => 'e-code-15a'}
|
132
|
+
session_id = @proctorserv_api.make_reservation options
|
133
|
+
response = @proctorserv_api.cancel_reservation({:session_id => session_id})
|
134
|
+
response.should == false
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: proctorserv-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Max Lahey
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rest-client
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.7
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.7
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.1'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.1'
|
46
|
+
description: Supports listing timeslots that are available to schedule a test session
|
47
|
+
for, scheduling a test session, and canceling a test session.
|
48
|
+
email:
|
49
|
+
- max@proctorcam.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/proctorserv_api/exceptions/invalid_client_exception.rb
|
55
|
+
- lib/proctorserv_api/exceptions/invalid_ip_exception.rb
|
56
|
+
- lib/proctorserv_api/exceptions/invalid_signature_exception.rb
|
57
|
+
- lib/proctorserv_api/exceptions/missing_required_parameter_exception.rb
|
58
|
+
- lib/proctorserv_api/hashed_authenticator.rb
|
59
|
+
- lib/proctorserv_api/rest_request_client.rb
|
60
|
+
- lib/proctorserv_api/version.rb
|
61
|
+
- lib/proctorserv_api.rb
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- spec/proctorserv_api_spec.rb
|
65
|
+
homepage: http://github.com/proctorcam/proctorserv-api
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.23
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: A ruby wrapper around the Proctorserve scheduling API.
|
89
|
+
test_files:
|
90
|
+
- spec/proctorserv_api_spec.rb
|
91
|
+
has_rdoc:
|