d2l_api 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,135 @@
1
+ require_relative 'requests'
2
+ ########################
3
+ # COURSE TEMPLATES:#####
4
+ ########################
5
+
6
+ # This method creates a course template using a merged payload between a
7
+ # pre-formatted payload and the argument "course_template_data". Upon this merge
8
+ # the path is defined for the POST http method that is then executed to create
9
+ # the course_template object.
10
+ # Required: "Name", "Code"
11
+ # /d2l/api/lp/(version)/coursetemplates/ [POST]
12
+ def create_course_template(course_template_data)
13
+ # ForceLocale- course override the user’s locale preference
14
+ # Path- root path to use for this course offering’s course content
15
+ # if your back-end service has path enforcement set on for
16
+ # new org units, leave this property as an empty string
17
+ # Define a valid, empty payload and merge! with the user_data. Print it.
18
+ payload = { 'Name' => '', # String
19
+ 'Code' => 'off_SEMESTERCODE_STARNUM', # String
20
+ 'Path' => '', # String
21
+ 'ParentOrgUnitIds' => [99_989], # number: D2L_ID
22
+ }.merge!(course_template_data)
23
+ puts "Creating Course Template:"
24
+ ap payload
25
+ # Define a path referencing the courses path
26
+ path = "/d2l/api/lp/#{$version}/coursetemplates/"
27
+ _post(path, payload)
28
+ puts '[+] Course template creation completed successfully'.green
29
+ end
30
+
31
+ # Retrieves a course template based upon an explicitly defined course template
32
+ # org_unit_id or Identifier. This is done by using the identifier as a component
33
+ # of the path, and then performing a GET http method that is then returned.
34
+ #
35
+ # returns: JSON course template data
36
+ # /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [GET]
37
+ def get_course_template(org_unit_id)
38
+ path = "/d2l/api/lp/#{$version}/coursetemplates/" + org_unit_id.to_s
39
+ _get(path)
40
+ end
41
+
42
+ # Instead of explicitly retrieving a single course template, this method uses
43
+ # the routing table to retrieve all of the organizations descendants with the
44
+ # outTypeId of 2. What this means is that it is literally retrieving any and all
45
+ # course templates that have an ancestor of the organization...which should be
46
+ # all of them.
47
+ #
48
+ # returns: JSON array of course template data objects
49
+ def get_all_course_templates
50
+ path = "/d2l/api/lp/#{$version}/orgstructure/6606/descendants/?ouTypeId=2"
51
+ _get(path)
52
+ end
53
+
54
+ # This method retrieves all course templates that have a specific string, as
55
+ # specified by org_unit_name, within their names. This is done by first defining
56
+ # that none are found yet and initializing an empty array. Then, by searching
57
+ # through all course templates for ones that do have a particular string within
58
+ # their name, the matches are pushed into the previously empty array of matches.
59
+ # This array is subsequently returned; if none were found, a message is returned
60
+ #
61
+ # returns: JSON array of matching course template data objects
62
+ def get_course_template_by_name(org_unit_name)
63
+ course_template_not_found = true
64
+ course_template_results = []
65
+ puts "[+] Searching for templates using search string: \'#{org_unit_name}\'".yellow
66
+ results = get_all_course_templates
67
+ results.each do |x|
68
+ if x['Name'].downcase.include? org_unit_name.downcase
69
+ course_template_not_found = false
70
+ course_template_results.push(x)
71
+ end
72
+ end
73
+ if course_template_not_found
74
+ puts '[-] No templates could be found based upon the search string.'.yellow
75
+ end
76
+ course_template_results
77
+ end
78
+
79
+ # Moreso a helper method, but this really just returns the schema of the
80
+ # course templates. This is predefined in the routing table, and retrieved via
81
+ # a GET http method.
82
+ #
83
+ # returns: JSON of course templates schema
84
+ # /d2l/api/lp/(version)/coursetemplates/schema [GET]
85
+ def get_course_templates_schema
86
+ path = "/d2l/api/lp/#{$version}/coursetemplates/schema"
87
+ _get(path)
88
+ end
89
+
90
+ # This is the primary method utilized to update course templates. As only the
91
+ # Name and the Code can be changed in an update, they are pre-defined to
92
+ # conform to the required update data. The update is then performed via a
93
+ # PUT http method that is executed using a path referencing the course template.
94
+ # /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [PUT]
95
+ def update_course_template(org_unit_id, new_data)
96
+ # Define a valid, empty payload and merge! with the new data.
97
+ payload = { 'Name' => '', # String
98
+ 'Code' => 'off_SEMESTERCODE_STARNUM', # String
99
+ }.merge!(new_data)
100
+ puts "Updating course template #{org_unit_id}"
101
+ #ap payload
102
+ # Define a path referencing the courses path
103
+ path = "/d2l/api/lp/#{$version}/coursetemplates/" + org_unit_id.to_s
104
+ _put(path, payload)
105
+ puts '[+] Course template update completed successfully'.green
106
+ end
107
+
108
+
109
+ # Simply, a course template can be deleted by refencing it using its Identifier
110
+ # as an argument for this method. The argument is then used to refernce the obj
111
+ # by a path and then the path is passed in for a delete http method.
112
+ # /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [DELETE]
113
+ def delete_course_template(org_unit_id)
114
+ path = "/d2l/api/lp/#{$version}/coursetemplates/" + org_unit_id.to_s
115
+ _delete(path)
116
+ puts '[+] Course template data deleted successfully'.green
117
+ end
118
+
119
+ # As a more streamlined approach to deleting many course templates conforming to
120
+ # a particular naming style, this function performs deletions based on a string.
121
+ # Using the name argument, +get_course_template_by_name+ is called in order to
122
+ # retrieve all matching templates. They are then deleted by referencing each
123
+ # of their Identifiers as arguments for +delete_course_template+.
124
+ def delete_all_course_templates_with_name(name)
125
+ puts "[!] Deleting all course templates with the name: #{name}"
126
+ get_course_template_by_name(name).each do |course_template|
127
+ puts "[!] Deleting the following course:".red
128
+ ap course_template
129
+ delete_course_template(course_template["Identifier"])
130
+ end
131
+ end
132
+ #TO DO:
133
+ def delete_course_templates_by_regex(regex)
134
+
135
+ end
@@ -0,0 +1,185 @@
1
+ require_relative 'requests'
2
+ ########################
3
+ # Org Units:############
4
+ ########################
5
+
6
+ # gets all descendents of a particular org unit, as referenced by the
7
+ # "org_unit_id" argument. A get request is then performed by a preformatted
8
+ # path.
9
+ def get_org_unit_descendants(org_unit_id)
10
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/descendants/"
11
+ _get(path)
12
+ # return json of org_unit descendants
13
+ end
14
+
15
+ def get_paged_org_unit_descendants(org_unit_id)
16
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/descendants/paged/"
17
+ _get(path)
18
+ # return json of org_unit descendants
19
+ end
20
+
21
+ # gets all parents of a particular org unit, as referenced by the
22
+ # "org_unit_id" argument. A get request is then performed by a preformatted
23
+ # path.
24
+ def get_org_unit_parents(org_unit_id)
25
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/parents/"
26
+ _get(path)
27
+ # return json of org_unit parents
28
+ end
29
+
30
+ def add_parent_to_org_unit(parent_ou_id, child_ou_id)
31
+ # Must follow structure of data
32
+ # (course <-- semester <== org -->custom dept--> dept -->templates--> courses)
33
+ # Refer to valence documentation for further structural understanding..
34
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{child_ou_id}/parents/"
35
+ _post(path, parent_ou_id)
36
+ # return json of org_unit parents
37
+ end
38
+
39
+ def get_org_unit_ancestors(org_unit_id)
40
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/ancestors/"
41
+ _get(path)
42
+ # return json of org_unit parents
43
+ end
44
+
45
+ # gets all children of a particular org unit, as referenced by the
46
+ # "org_unit_id" argument. A get request is then performed by a preformatted
47
+ # path.
48
+ def get_org_unit_children(org_unit_id)
49
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/children/"
50
+ _get(path)
51
+ # return json of org_unit children
52
+ end
53
+
54
+ def get_paged_org_unit_children(org_unit_id)
55
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/children/paged/"
56
+ _get(path)
57
+ # return json of org_unit children
58
+ end
59
+
60
+ # gets all properties of a particular org unit, as referenced by the
61
+ # "org_unit_id" argument. A get request is then performed by a preformatted
62
+ # path.
63
+ def get_org_unit_properties(org_unit_id)
64
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}"
65
+ _get(path)
66
+ # return json of org_unit properties
67
+ end
68
+
69
+ def delete_relationship_of_child_with_parent(parent_ou_id, child_ou_id)
70
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{parent_ou_id}/children/#{child_ou_id}"
71
+ _delete(path)
72
+ end
73
+
74
+ def delete_relationship_of_parent_with_child(parent_ou_id, child_ou_id)
75
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{child_ou_id}/parents/#{parent_ou_id}"
76
+ _delete(path)
77
+ end
78
+
79
+ def get_all_childless_org_units
80
+ path = "/d2l/api/lp/#{$version}/orgstructure/childless/"
81
+ _get(path)
82
+ # ONLY RETRIEVES FIRST 100
83
+ end
84
+
85
+ def get_all_orphans
86
+ path = "/d2l/api/lp/#{$version}/orgstructure/orphans/"
87
+ _get(path)
88
+ end
89
+
90
+ # Adds a child to the org unit by using org_unit_id to reference the soon-to-be
91
+ # parent of the child_org_unit and referencing the soon-to-be child through the
92
+ # child_org_unit_id argument. Then, a path is created to reference the children
93
+ # of the soon-to-be parent and executing a post http method that adds the child.
94
+ #
95
+ # TL;DR, this adds a child org_unit to the children of an org_unit.
96
+ def add_child_org_unit(org_unit_id, child_org_unit_id)
97
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}/children/"
98
+ _post(path, child_org_unit_id)
99
+ end
100
+
101
+ def get_recycled_org_units
102
+ path = "/d2l/api/lp/#{$version}/orgstructure/recyclebin/"
103
+ _get(path)
104
+ # GETS ONLY FIRST 100
105
+ end
106
+
107
+ # An org unit is recycled by executing a POST http method and recycling it. The
108
+ # path for the recycling is created using the org_unit_id argument and then the
109
+ # post method is executed afterwards.
110
+ def recycle_org_unit(org_unit_id)
111
+ path = "/d2l/api/lp/#{$version}/orgstructure/recyclebin/#{org_unit_id}/recycle"
112
+ _post(path, {})
113
+ end
114
+
115
+ def delete_recycled_org_unit(org_unit_id)
116
+ path = "/d2l/api/lp/#{$version}/orgstructure/recyclebin/#{org_unit_id}"
117
+ _delete(path)
118
+ end
119
+
120
+ def restore_recycled_org_unit(org_unit_id)
121
+ path = "/d2l/api/lp/#{$version}/orgstructure/recyclebin/#{org_unit_id}/restore"
122
+ _post(path, {})
123
+ end
124
+
125
+ # Functions considered for basic added functionality to api, not sure if needed.
126
+ def create_custom_org_unit(org_unit_data)
127
+ # Requires the type to have the correct parent. This will work fine in this
128
+ # sample, as the department (101) can have the parent Organiation (6606)
129
+ payload = { 'Type' => 101, # Number:D2LID
130
+ 'Name' => 'custom_ou_name', # String
131
+ 'Code' => 'custom_ou_code', # String
132
+ 'Parents' => [6606], # Number:D2LID
133
+ }.merge!(org_unit_data)
134
+ path = "/d2l/api/lp/#{$version}/orgstructure/"
135
+ _post(path, payload)
136
+ end
137
+
138
+ def update_org_unit(org_unit_id, org_unit_data)
139
+ previous_data = get_org_unit_properties(org_unit_id)
140
+ payload = { # Can only update NAME, CODE, and PATH variables
141
+ 'Identifier' => org_unit_id.to_s, # String: D2LID // DO NOT CHANGE
142
+ 'Name' => previous_data['Name'], # String
143
+ # String #YearNUM where NUM{sp:01,su:06,fl:08} | nil
144
+ 'Code' => previous_data['Code'],
145
+ # String: /content/enforced/IDENTIFIER-CODE/
146
+ 'Path' => "/content/enforced/#{org_unit_id}-#{previous_data['Code']}/",
147
+ 'Type' => previous_data['Type']
148
+ # example:
149
+ # { # DO NOT CHANGE THESE
150
+ # 'Id' => 5, # <number:D2LID>
151
+ # 'Code' => 'Semester', # <string>
152
+ # 'Name' => 'Semester', # <string>
153
+ # }
154
+ }.merge!(org_unit_data)
155
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}"
156
+ puts '[-] Attempting put request (updating orgunit)...'
157
+ _put(path, payload)
158
+ puts '[+] Semester update completed successfully'.green
159
+ end
160
+
161
+ # Retrieves the organization info. Only gets a small amount of information,
162
+ # but may be useful in some instances.
163
+ def get_organization_info
164
+ path = "/d2l/api/lp/#{$version}/organization/info"
165
+ _get(path)
166
+ end
167
+
168
+ def get_all_org_units_by_type_id(outype_id)
169
+ path = "/d2l/api/lp/#{$version}/orgstructure/6606/children/?ouTypeId=#{outype_id}"
170
+ _get(path)
171
+ end
172
+
173
+ # This retrieves information about a partituclar org unit type, referenced via
174
+ # the outype_id argument. This is then returned as a JSON object.
175
+ def get_outype(outype_id)
176
+ path = "/d2l/api/lp/#{$version}/outypes/#{outype_id}"
177
+ _get(path)
178
+ end
179
+
180
+ # retrieves all outypes that are known and visible. This is returned as a JSON
181
+ # array of orgunittype data blocks.
182
+ def get_all_outypes
183
+ path = "/d2l/api/lp/#{$version}/outypes/"
184
+ _get(path)
185
+ end
@@ -0,0 +1,92 @@
1
+ require_relative 'auth'
2
+
3
+ ########################
4
+ # QUERIES/RESPONSE:#####
5
+ ########################
6
+
7
+ # performs a get request on a particular path of the host.
8
+ # To do this, a uniform resource identifier string is created using the path and
9
+ # specifying that this is a get request. Then, the RestClient get method is
10
+ # used to retrieve the data and parse it as a JSON. Then, the parsed response is
11
+ # returned. Otherwise, nothing is returned and the response code is printed
12
+ #
13
+ # returns: JSON parsed response.
14
+ def _get(path)
15
+ uri_string = create_authenticated_uri(path, 'GET')
16
+ RestClient.get(uri_string) do |response, _request, _result|
17
+ case response.code
18
+ when 200
19
+ # ap JSON.parse(response) # Here is the JSON fmt'd response printed
20
+ JSON.parse(response)
21
+ else
22
+ display_response_code(response.code)
23
+ end
24
+ end
25
+ end
26
+
27
+ # performs a post request using the path and the payload arguments. First, an
28
+ # authenticated uri is created to reference a particular resource. Then, the
29
+ # post method is executed using the payload and specifying that it is formatted
30
+ # as JSON.
31
+ def _post(path, payload)
32
+ auth_uri = create_authenticated_uri(path, 'POST')
33
+ RestClient.post(auth_uri, payload.to_json, content_type: :json)
34
+ end
35
+
36
+ # performs a put request using the path and the payload arguments. After first
37
+ # creating an authenticated uri, the put request is performed using the
38
+ # authenticated uri, the payload argument, and specifying that the payload is
39
+ # formatted in JSON.
40
+ def _put(path, payload)
41
+ auth_uri = create_authenticated_uri(path, 'PUT')
42
+ # Perform the put action, updating the data; Provide feedback to client.
43
+ RestClient.put(auth_uri, payload.to_json, content_type: :json)
44
+ end
45
+
46
+ # Performs a delete request by creating an authenticated uri and using the
47
+ # RestClient delete method and specifying the content_type as being JSON.
48
+ def _delete(path)
49
+ auth_uri = create_authenticated_uri(path, 'DELETE')
50
+ RestClient.delete(auth_uri, content_type: :json)
51
+ end
52
+
53
+ # based upon the specific code that is returned from the http method, this
54
+ # displays the response, in the case that it is an error within the request
55
+ # or the server. This is simply informative and assists in describing the
56
+ # lacking response information from the valence api. In the case of a Bad
57
+ # Request, it is likely that it cannot be further specified without looking
58
+ # back at the d2l_api documentation or looking at the documentation on
59
+ # the docs.valence.desire2learn.com website.
60
+ def display_response_code(code)
61
+ case code
62
+ when 400
63
+ puts '[!] 400: Bad Request'
64
+ when 401
65
+ puts '[!] 401: Unauthorized'
66
+
67
+ when 403
68
+ print '[!] Error Code Forbidden 403: accessing the page or resource '\
69
+ 'you were trying to reach is absolutely forbidden for some reason.'
70
+ when 404
71
+ puts '[!] 404: Not Found'
72
+ when 405
73
+ puts '[!] 405: Method Not Allowed'
74
+ when 406
75
+ puts 'Unacceptable Type'\
76
+ 'Unable to provide content type matching the client\'s Accept header.'
77
+ when 412
78
+ puts '[!] 412: Precondition failed\n'\
79
+ 'Unsupported or invalid parameters, or missing required parameters.'
80
+ when 415
81
+ puts '[!] 415: Unsupported Media Type'\
82
+ 'A PUT or POST payload cannot be accepted.'
83
+ when 423
84
+ puts '[!] 423'
85
+ when 500
86
+ puts '[!] 500: General Service Error\n'\
87
+ 'Empty response body. The service has encountered an unexpected'\
88
+ 'state and cannot continue to handle your action request.'
89
+ when 504
90
+ puts '[!] 504: Service Error'
91
+ end
92
+ end
@@ -0,0 +1,120 @@
1
+ require_relative "requests"
2
+ ########################
3
+ # SEMESTER:#############
4
+ ########################
5
+
6
+ # Creates a semester based upon a merged result from merging a preformatted
7
+ # payload and the inputed semester data. This is then created server-side
8
+ # via executing a POST http method using a predefined path and the new payload.
9
+ def create_semester_data(semester_data)
10
+ # Define a valid, empty payload and merge! with the semester_data. Print it.
11
+ payload = { 'Type' => 5, # Number:D2LID
12
+ 'Name' => 'Winter 2013 Semester', # String
13
+ 'Code' => '201701', # String #YearNUM where NUM{sp:01,su:06,fl:08}
14
+ 'Parents' => [6606], # ARR of Number:D2LID
15
+ }.merge!(semester_data)
16
+ #ap payload
17
+ path = "/d2l/api/lp/#{$version}/orgstructure/"
18
+ _post(path, payload)
19
+ puts '[+] Semester creation completed successfully'.green
20
+ end
21
+
22
+ # This retrieves all semesters via getting all children from the main
23
+ # organization and filtering them by the default data type of semesters.
24
+ #
25
+ # Returns: Array of all semester JSON formatted data
26
+ def get_all_semesters
27
+ path = "/d2l/api/lp/#{$version}/orgstructure/6606/children/?ouTypeId=5"
28
+ _get(path)
29
+ end
30
+
31
+ def get_semester_by_id(org_unit_id)
32
+ path = "/d2l/api/lp/#{$version}/orgstructure/" + org_unit_id.to_s
33
+ _get(path)
34
+ # return json of org_unit properties
35
+ end
36
+ # Rather than retrieving all semesters, this retrieves all semesters by a
37
+ # particular string. First, a boolean is created where it is assumed the
38
+ # semester is not found. Then an array is created with all the 'found' semesters
39
+ # assuming none are found, but providing the structure to still return uniform
40
+ # data that can be iterated. Then, by iterating through all semesters and only
41
+ # storing ones that conform to the search string, all matched semesters are then
42
+ # returned.
43
+ #
44
+ # Returns: Array of all semester JSON formatted data (with search string in name)
45
+ def get_semester_by_name(search_string)
46
+ semester_not_found = true
47
+ semester_results = []
48
+ puts "[+] Searching for semesters using search string: \'#{search_string}\'".yellow
49
+ results = get_all_semesters
50
+ results.each do |x|
51
+ if x['Name'].downcase.include? search_string.downcase
52
+ semester_not_found = false
53
+ semester_results.push(x)
54
+ end
55
+ end
56
+ if semester_not_found
57
+ puts '[-] No semesters could be found based upon the search string.'.yellow
58
+ end
59
+ semester_results
60
+ end
61
+
62
+ # This is simply a helper function that can assist in preformatting a path
63
+ # that conforms to the required 'Path' for updating semester data.
64
+ #
65
+ # returns: preformatted semester 'Path' string
66
+ def create_semester_formatted_path(org_id, code)
67
+ "/content/enforced/#{org_id}-#{code}/"
68
+ end
69
+
70
+ # Updates a semester's data via merging a preformatted payload with the new
71
+ # semester data. The 'path' is then initialized using a defined org_unit_id
72
+ # and the semester is then updated via the newly defined payload and the path.
73
+ def update_semester_data(org_unit_id, semester_data)
74
+ # Define a valid, empty payload and merge! with the semester_data. Print it.
75
+ payload = { # Can only update NAME, CODE, and PATH variables
76
+ 'Identifier' => org_unit_id.to_s, # String: D2LID // DO NOT CHANGE
77
+ 'Name' => 'NAME', # String
78
+ # String #YearNUM where NUM{sp:01,su:06,fl:08} | nil
79
+ 'Code' => 'REQUIRED',
80
+ # String: /content/enforced/IDENTIFIER-CODE/
81
+ 'Path' => create_semester_formatted_path(org_unit_id.to_s, 'YEAR01'),
82
+ 'Type' => { # DO NOT CHANGE THESE
83
+ 'Id' => 5, # <number:D2LID>
84
+ 'Code' => 'Semester', # <string>
85
+ 'Name' => 'Semester', # <string>
86
+ }
87
+ }.merge!(semester_data)
88
+ # print out the projected new data
89
+ #puts '[-] New Semester Data:'.yellow
90
+ #ap payload
91
+ # Define a path referencing the course data using the course_id
92
+ path = "/d2l/api/lp/#{$version}/orgstructure/#{org_unit_id}"
93
+ puts '[-] Attempting put request (updating orgunit)...'
94
+ _put(path, payload)
95
+ puts '[+] Semester update completed successfully'.green
96
+ end
97
+
98
+ # This function provides the means to put the semester data into the recycling
99
+ # bin. A path is then created using the org_unit_id argument. Once this is done
100
+ # the post http method is then completed in order to officially recycle the data
101
+ def recycle_semester_data(org_unit_id)
102
+ # Define a path referencing the user data using the user_id
103
+ puts '[!] Attempting to recycle Semester data referenced by id: ' + org_unit_id.to_s
104
+ path = "/d2l/api/lp/#{$version}/orgstructure/recyclebin/" + org_unit_id.to_s + '/recycle' # setup user path
105
+ _post(path, {})
106
+ puts '[+] Semester data recycled successfully'.green
107
+ end
108
+
109
+ # Rather than recycling a semester by a specific id, this function can recycle
110
+ # all semesters that match a particular name. The names must be exactly the same
111
+ # as the "name" argument. Each name that is the exact same as this argument is
112
+ # then recycled, iteratively.
113
+ def recycle_semester_by_name(name)
114
+ results = get_semester_by_name(name)
115
+ results.each do |semester_match|
116
+ if semester_match["Name"] == name
117
+ recycle_semester_data(semester_match["Identifier"])
118
+ end
119
+ end
120
+ end