CollegiateLink 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ require 'digest'
2
+ require 'net/https'
3
+ require 'net/ssh'
4
+ require 'guid'
5
+ require 'nokogiri'
6
+ require 'happymapper'
7
+
8
+ require 'collegiatelink/response'
9
+ require 'collegiatelink/request'
10
+ require 'collegiatelink/client'
11
+ require 'collegiatelink/organization'
12
+
13
+ ##
14
+ # This gem provides a CollegiateLink API client in Ruby. It's still under
15
+ # development, so let me know if you plan to use it. (tomdooner@gmail.com)
16
+ #
17
+ module CollegiateLink
18
+ # URL base for all requests. The API key will be substituted into this when
19
+ # the request is made.
20
+ URL_BASE = 'https://%s.collegiatelink.net/ws/'
21
+
22
+ # Currently-supported CollegiateLink action request types
23
+ ACTIONS = [
24
+ 'organization/list',
25
+ 'organization/roster',
26
+ #'user/memberships',
27
+ #'user/position',
28
+ 'event/list',
29
+ ]
30
+
31
+ # Raised when performing a CollegiateLink::Request for an action that is not
32
+ # supported by the gem (as defined by CollegiateLink::ACTIONS).
33
+ class UnknownAction < Exception; end
34
+
35
+ # Raised when something unexpected goes wrong.
36
+ class UnknownException < Exception; end
37
+
38
+ # Raised when the CollegiateLink::Request HTTP request fails.
39
+ class NetworkException < Exception; end
40
+
41
+ # Raised when CollegiateLink returns that the combination of IP and SSL fails.
42
+ class AuthenticationException < Exception; end
43
+
44
+ # Raised for the CollegiateLink VALIDATION_ERROR case.
45
+ class ValidationException < Exception; end
46
+ end
@@ -0,0 +1,79 @@
1
+ module CollegiateLink
2
+
3
+ # The Client is what makes the requests to CollegiateLink.
4
+ class Client
5
+ ##
6
+ # Creates a CollegiateLink client.
7
+ #
8
+ # ==== Parameters:
9
+ # * +apikey+ - The CollegiateLink "apikey" for your institution. For example, at Case Western Reserve University we go to http://casewestern.collegiatelink.net, so our "apikey" is "casewestern"
10
+ # * +ip+ - The IP address that the +sharedkey+ is approved for.
11
+ # * +sharedkey+ - The shared key granted by CollegiateLink to your IP and institution.
12
+ #
13
+ def initialize(apikey, ip, sharedkey)
14
+ @params = {
15
+ apikey: apikey,
16
+ ip: ip
17
+ }
18
+ @opts = {
19
+ sharedkey: sharedkey
20
+ }
21
+ end
22
+
23
+ ##
24
+ # Return a complete list of CollegiateLink::Organization instances for your
25
+ # institution.
26
+ #
27
+ # ==== Required Parameters:
28
+ # None
29
+ #
30
+ # ==== Optional Parameters:
31
+ # See CollegiateLink::Request#initialize for a list of optional parameters
32
+ #
33
+ def organizations(params = {})
34
+ orgs = request('organization/list', params)
35
+ orgs.map{ |o| CollegiateLink::Organization.parse(o.to_s) }
36
+ end
37
+
38
+ ##
39
+ # Return a complete list of CollegiateLink::Event instances for your
40
+ # institution.
41
+ #
42
+ # ==== Required Parameters:
43
+ # * <tt>:startdate</tt> - Time instance or the Unix integral timestamp for the beginning of results.
44
+ # * <tt>:enddate</tt> - Time instance or the Unix integral timestamp for the end of results.
45
+ #
46
+ # ==== Optional Parameters:
47
+ # See CollegiateLink::Request#initialize for a list of additional parameters
48
+ # that requests can use.
49
+ def events(params = {})
50
+ # Default to showing events in the past 7 days
51
+ seven_days = 7 * 24 * 60 * 60
52
+ params[:startdate] ||= (Time.now.to_i - seven_days)
53
+ params[:enddate] ||= (Time.now.to_i)
54
+
55
+ # Convert to milliseconds...
56
+ params[:startdate] = params[:startdate].to_i * 1000
57
+ params[:enddate] = params[:enddate].to_i * 1000
58
+
59
+ events = request('event/list', params)
60
+ events.map{ |o| CollegiateLink::Event.parse(o.to_s) }
61
+ end
62
+
63
+ private
64
+
65
+ def request(action, params = {})
66
+ cl_request = CollegiateLink::Request.new(action, params.merge(@params), @opts)
67
+ cl_resp = cl_request.perform
68
+
69
+ all_items = cl_resp.items
70
+
71
+ while cl_resp.has_next_page?
72
+ cl_resp = cl_request.request_for_next_page.perform
73
+ all_items += cl_resp.items
74
+ end
75
+
76
+ all_items
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,98 @@
1
+ module CollegiateLink
2
+ ##
3
+ # Address object returned by CollegiateLink as part of an Organization or
4
+ # Event record.
5
+ #
6
+ # ==== Properties:
7
+ # * <tt>:city </tt> - String
8
+ # * <tt>:country </tt> - String
9
+ # * <tt>:postalcode</tt> - String
10
+ # * <tt>:state </tt> - String
11
+ # * <tt>:street1 </tt> - String
12
+ # * <tt>:street2 </tt> - String
13
+ # * <tt>:type </tt> - Integer
14
+ class Address
15
+ include HappyMapper
16
+
17
+ element :city, String
18
+ element :country, String
19
+ element :postalcode, String
20
+ element :state, String
21
+ element :street1, String
22
+ element :street2, String
23
+ element :type, Integer
24
+ end
25
+
26
+ ##
27
+ # Category object returned by CollegiateLink as part of an Organization record
28
+ # See: # http://support.collegiatelink.net/entries/332558-web-services-developer-documentation#orgList
29
+ #
30
+ # ==== Properties:
31
+ # * <tt>:id </tt> - Integer
32
+ # * <tt>:ishidden</tt> - String
33
+ # * <tt>:name </tt> - String
34
+ class Category
35
+ include HappyMapper
36
+
37
+ element :id, Integer
38
+ element :ishidden, String
39
+ element :name, String
40
+ end
41
+
42
+ ##
43
+ # An Organization record returned by CollegiateLink
44
+ # See: http://support.collegiatelink.net/entries/332558-web-services-developer-documentation#orgList
45
+ #
46
+ # ==== Properties:
47
+ # * <tt>:id </tt> - Integer
48
+ # * <tt>:ishidden</tt> - String
49
+ # * <tt>:name </tt> - String
50
+ # * <tt>:addresses </tt> - Array of Address objects
51
+ # * <tt>:categories </tt> - Array of Category objects
52
+ class Organization
53
+ include HappyMapper
54
+
55
+ element :id, Integer
56
+ element :parentId, Integer
57
+ element :name, String
58
+ element :description, String
59
+ element :shortName, String
60
+ element :siteUrl, String
61
+ element :status, String
62
+ element :type, String
63
+ has_many :addresses, Address
64
+ has_many :categories, Category
65
+ end
66
+
67
+ ##
68
+ # An Event record returned by CollegiateLink
69
+ # See: http://support.collegiatelink.net/entries/332558-web-services-developer-documentation#eventList
70
+ #
71
+ # ==== Properties:
72
+ # * <tt>:id </tt> - Integer
73
+ # * <tt>:name </tt> - String
74
+ # * <tt>:description</tt> - String
75
+ # * <tt>:startDate </tt> - Integer
76
+ # * <tt>:endDate </tt> - Integer
77
+ # * <tt>:location </tt> - String
78
+ # * <tt>:status </tt> - String
79
+ # * <tt>:urlLarge </tt> - String
80
+ # * <tt>:urlSmall </tt> - String
81
+ # * <tt>:organization</tt> - The hosting Organization
82
+ #
83
+ class Event
84
+ include HappyMapper
85
+
86
+ element :id, Integer
87
+ element :name, String
88
+ element :description, String
89
+ element :startDate, Integer
90
+ element :endDate, Integer
91
+ element :location, String
92
+ element :status, String
93
+ element :urlLarge, String
94
+ element :urlSmall, String
95
+
96
+ has_one :organization, Organization
97
+ end
98
+ end
@@ -0,0 +1,122 @@
1
+ module CollegiateLink
2
+ ##
3
+ # Represents a single HTTP request to the CollegiateLink API.
4
+ #
5
+ class Request
6
+ ##
7
+ # Create a new Request instance. See +CollegiateLink::ACTIONS+ for a list of
8
+ # eligible actions.
9
+ #
10
+ # ==== Parameters:
11
+ # All parameters are provided in the URL of the request to CollegiateLink.
12
+ #
13
+ # * <tt>:page</tt> - The desired page number of results. (default = 1)
14
+ # * <tt>:pagesize</tt> - The desired number of records to return (default = 100)
15
+ # * <tt>:modelformatting</tt> - Either "normal" or "humanreadable". (default = "normal")
16
+ #
17
+ # ==== Options:
18
+ # Options are ways to pass in additional parameters about the request. The
19
+ # following options are supported.
20
+ #
21
+ # * <tt>:sharedkey</tt> - (Required) The shared key which is used, but not included in the URL of requests
22
+ # * <tt>:debug</tt> - Print debug information as the request happens.
23
+ #
24
+ def initialize(action, params = {}, opts = {})
25
+ raise UnknownAction unless ACTIONS.include?(action)
26
+ raise AuthenticationException unless opts.include?(:sharedkey)
27
+
28
+ @action = action
29
+ @params = params
30
+ @opts = opts
31
+
32
+ # URL Query String Parameters
33
+ @params[:page] ||= 1
34
+ @params[:pagesize] ||= 100
35
+ @params[:modelformatting] ||= 'normal'
36
+ end
37
+
38
+ ##
39
+ # Execute the request by sending the HTTP request.
40
+ #
41
+ def perform
42
+ # First, add the time-sensitive bits...
43
+ request_params = @params.merge({
44
+ time: Time.now.to_i * 1000,
45
+ random: Guid.new
46
+ })
47
+ request_params[:hash] = hash(request_params.merge(sharedkey: @opts[:sharedkey]))
48
+
49
+ # Then, compute the URL...
50
+ url = URI([
51
+ URL_BASE % @params[:apikey]
52
+ @action
53
+ '?'
54
+ URI.encode_www_form(request_params)
55
+ ].join)
56
+
57
+ puts "requesting: \n#{url}" if @opts[:debug]
58
+
59
+ # Make the Request!
60
+ res = Net::HTTP.new(url.host, url.port)
61
+ res.use_ssl = true
62
+ res.verify_mode = OpenSSL::SSL::VERIFY_NONE
63
+
64
+ resp = res.start { |h|
65
+ h.request_get(url.to_s);
66
+ }
67
+
68
+ case resp
69
+ when Net::HTTPSuccess
70
+ return parse_response(resp.body)
71
+ when Net::HTTPError
72
+ raise NetworkException
73
+ else
74
+ raise UnknownException
75
+ end
76
+ end
77
+
78
+ ##
79
+ # Increment the page number and return a request instance that would receive
80
+ # the next bunch of pages.
81
+ #
82
+ def request_for_next_page
83
+ Request.new(@action, @params.merge(page: @params[:page] + 1), @opts)
84
+ end
85
+
86
+ private
87
+
88
+ def parse_response(body)
89
+ d = Nokogiri::XML::Document.parse(body)
90
+
91
+ # Parse exceptions
92
+ if d.xpath("//results/error").length > 0
93
+ type = d.xpath('//results/error/type').inner_html
94
+ message = d.xpath('//results/error/message').inner_html
95
+ case type
96
+ when 'AUTHENTICATION_ERROR'
97
+ raise AuthenticationException, message
98
+ when 'VALIDATION_ERROR'
99
+ raise ValidationException, message
100
+ when 'UNKNOWN_SERVICE'
101
+ raise UnknownAction, message
102
+ when 'GENERAL_ERROR'
103
+ raise UnknownException, message
104
+ else
105
+ raise UnknownException, message
106
+ end
107
+ end
108
+
109
+ Response.new(d)
110
+ end
111
+
112
+ def hash(params = {})
113
+ Digest::MD5.hexdigest [
114
+ params[:apikey],
115
+ params[:ip],
116
+ params[:time].to_i,
117
+ params[:random],
118
+ params[:sharedkey],
119
+ ].join
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,30 @@
1
+ module CollegiateLink
2
+ ##
3
+ # Container for the XML gunk returned from CollegiateLink
4
+ #
5
+ class Response
6
+ # Create the response.
7
+ #
8
+ # ==== Parameters:
9
+ # * +document+ - The Nokogiri document of the returned XML
10
+ def initialize(document)
11
+ raise UnknownException unless document.xpath('//results/page')
12
+
13
+ @document = document
14
+ @page_size = document.xpath('//results/page/pageSize').inner_html.to_i
15
+ @page_number = document.xpath('//results/page/pageNumber').inner_html.to_i
16
+ @total_items = document.xpath('//results/page/totalItems').inner_html.to_i
17
+ @total_pages = document.xpath('//results/page/totalPages').inner_html.to_i
18
+ end
19
+
20
+ # Extract the items from the response. Returns a Nokogiri NodeSet.
21
+ def items
22
+ @document.xpath('//results/page/items/*')
23
+ end
24
+
25
+ # Utility method to determine if the request has another page to retrieve.
26
+ def has_next_page?
27
+ @page_number < @total_pages
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: CollegiateLink
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tom Dooner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: guid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
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: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: happymapper
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: net-ssh
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description:
95
+ email: ted27@case.edu
96
+ executables: []
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - lib/collegiatelink.rb
101
+ - lib/collegiatelink/request.rb
102
+ - lib/collegiatelink/response.rb
103
+ - lib/collegiatelink/client.rb
104
+ - lib/collegiatelink/organization.rb
105
+ homepage:
106
+ licenses: []
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.24
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: CollegiateLink Client Gem
129
+ test_files: []
130
+ has_rdoc: