d2l_api 0.1.2

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