forked from MarkUsProject/Markus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add API to MarkUs. Add possibility to push test results and view them…
…/download them. See review 300. Closes ticket MarkUsProject#541
- Loading branch information
Severin Gehwolf
authored and
Severin Gehwolf
committed
Jan 10, 2010
1 parent
0341f9b
commit ba31d27
Showing
43 changed files
with
1,147 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
require 'base64' | ||
|
||
#=== Description | ||
# Scripting API handlers for MarkUs | ||
module Api | ||
|
||
#===Description | ||
# This is the parent class of all API controllers. | ||
# Shared functionality of all API controllers | ||
# should go here. | ||
class MainApiController < ActionController::Base | ||
|
||
before_filter :authenticate | ||
|
||
#=== Description | ||
# Dummy action (for authentication testing) | ||
# No public route matches this action. | ||
def index | ||
render :file => "#{RAILS_ROOT}/public/200.xml", :status => 200 | ||
end | ||
|
||
private | ||
#=== Description | ||
# Auth handler for the MarkUs API. It uses | ||
# the Authorization HTTP header to determine | ||
# the user who issued the request. With the Authorization | ||
# HTTP header comes a Base 64 encoded MD5 digest of the | ||
# user's private key. | ||
def authenticate | ||
auth_token = parse_auth_token(request.headers["HTTP_AUTHORIZATION"]) | ||
# pretend resource not found if missing or wrong authentication | ||
# is provided | ||
if auth_token.nil? | ||
render :file => "#{RAILS_ROOT}/public/403.xml", :status => 403 | ||
return | ||
end | ||
# Find user by api_key_md5 | ||
@current_user = User.find_by_api_key_md5(auth_token) | ||
if @current_user.nil? | ||
# Key does not exist, so bail out | ||
render :file => "#{RAILS_ROOT}/public/403.xml", :status => 403 | ||
return | ||
elsif @current_user.student? | ||
# API is available for TAs and Admins only | ||
render :file => "#{RAILS_ROOT}/public/403.xml", :status => 403 | ||
return | ||
end | ||
end | ||
|
||
|
||
#=== Description | ||
# Helper method for parsing the authentication token | ||
def parse_auth_token(token) | ||
return nil if token.nil? | ||
if !(token =~ /MarkUsAuth ([^\s,]+)/).nil? | ||
ret_token = $1 | ||
begin | ||
ret_token = Base64.decode64(ret_token).strip | ||
# we expect a MD5 sum string of length 32 | ||
return nil unless ret_token.length == 32 | ||
# now we are good, so it seems to be a valid | ||
# token | ||
return ret_token | ||
rescue Exception | ||
return nil | ||
end | ||
else | ||
return nil | ||
end | ||
end | ||
|
||
end | ||
|
||
end # end Api module |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
module Api | ||
|
||
#=== Description | ||
# Allows for pushing of test results into MarkUs (e.g. from automated test runs). | ||
# Uses Rails' RESTful routes (check 'rake routes' for the configured routes) | ||
class TestResultsController < MainApiController | ||
|
||
#=== Description | ||
# Triggered by a HTTP POST request to /api/test_results(.:format). | ||
# Creates a new TestResult instance. Requires the following parameters: | ||
# group_name: Name of the group to which the test result should be associated to | ||
# assignment: Short identifier of the assignment | ||
# filename: Filename of the test result | ||
# file_content: Content of the test results | ||
#=== Returns | ||
# An XML response, indicating the success/failure for the request | ||
def create | ||
if !request.post? | ||
# pretend this URL does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404 | ||
return | ||
end | ||
if !has_required_http_params_including_file_content?(params) | ||
# incomplete/invalid HTTP params | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# check if there's a valid submission | ||
submission = Submission.get_submission_by_group_and_assignment(params[:group_name], | ||
params[:assignment]) | ||
if submission.nil? | ||
# no such submission | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# Request seems good. Check if filename already exists. | ||
# If it does, update it instead of creating a new one. | ||
new_test_result = submission.test_results.find_by_filename(params[:filename]) | ||
if new_test_result.nil? | ||
if TestResult.create(:filename => params[:filename], | ||
:file_content => params[:file_content], | ||
:submission_id => submission.id) | ||
# All good, so return a success response | ||
render :file => "#{RAILS_ROOT}/public/200.xml", :status => 200 | ||
return | ||
else | ||
# Some other error occurred | ||
render :file => "#{RAILS_ROOT}/public/500.xml", :status => 500 | ||
return | ||
end | ||
else | ||
new_test_result.file_content = params[:file_content] | ||
if new_test_result.save | ||
# All good, so return a success response | ||
render :file => "#{RAILS_ROOT}/public/200.xml", :status => 200 | ||
return | ||
else | ||
# Some other error occurred | ||
render :file => "#{RAILS_ROOT}/public/500.xml", :status => 500 | ||
return | ||
end | ||
end | ||
end | ||
|
||
#=== Description | ||
# Triggered by a HTTP DELETE request to /api/test_results(.:format). | ||
# Deletes a TestResult instance. Requires the following parameters: | ||
# group_name: Name of the group to which the test result should be associated to | ||
# assignment: Short identifier of the assignment | ||
# filename: Filename of the test result to be deleted | ||
#=== Returns | ||
# An XML response, indicating the success/failure for the request | ||
def destroy | ||
if !request.delete? | ||
# pretend this URL does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404 | ||
return | ||
end | ||
if !has_required_http_params?(params) | ||
# incomplete/invalid HTTP params | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# check if there's a valid submission | ||
submission = Submission.get_submission_by_group_and_assignment(params[:group_name], | ||
params[:assignment]) | ||
if submission.nil? | ||
# no such submission | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# request seems good | ||
test_result = submission.test_results.find_by_filename(params[:filename]) | ||
if !test_result.nil? | ||
if test_result.destroy | ||
# Everything went fine; report success | ||
render :file => "#{RAILS_ROOT}/public/200.xml", :status => 200 | ||
return | ||
else | ||
# Some other error occurred | ||
render :file => "#{RAILS_ROOT}/public/500.xml", :status => 500 | ||
return | ||
end | ||
end | ||
# The test result in question does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.xml", :status => 404 | ||
return | ||
end | ||
|
||
#=== Description | ||
# Triggered by a HTTP PUT request to /api/test_results(.:format). | ||
# Updates (overwrites) a TestResult instance. Requires the following parameters: | ||
# group_name: Name of the group to which the test result should be associated to | ||
# assignment: Short identifier of the assignment | ||
# filename: Filename of the test result, which content should be updated | ||
# file_content: New content of the test result | ||
#=== Returns | ||
# An XML response, indicating the success/failure for the request | ||
def update | ||
if !request.put? | ||
# pretend this URL does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404 | ||
return | ||
end | ||
if !has_required_http_params_including_file_content?(params) | ||
# incomplete/invalid HTTP params | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# check if there's a valid submission | ||
submission = Submission.get_submission_by_group_and_assignment(params[:group_name], | ||
params[:assignment]) | ||
if submission.nil? | ||
# no such submission | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# request seems good | ||
test_result = submission.test_results.find_by_filename(params[:filename]) | ||
if !test_result.nil? | ||
if test_result.update_file_content(params[:file_content]) | ||
# Everything went fine; report success | ||
render :file => "#{RAILS_ROOT}/public/200.xml", :status => 200 | ||
return | ||
else | ||
# Some other error occurred | ||
render :file => "#{RAILS_ROOT}/public/500.xml", :status => 500 | ||
return | ||
end | ||
end | ||
# The test result in question does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.xml", :status => 404 | ||
return | ||
end | ||
|
||
#=== Description | ||
# Triggered by a HTTP GET request to /api/test_results(.:format). | ||
# Shows a TestResult instance. Requires the following parameters: | ||
# group_name: Name of the group to which the test result should be associated to | ||
# assignment: Short identifier of the assignment | ||
# filename: New filename of the test result | ||
#=== Returns | ||
# The content of the test result file in question | ||
def show | ||
if !request.get? | ||
# pretend this URL does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404 | ||
return | ||
end | ||
if !has_required_http_params?(params) | ||
# incomplete/invalid HTTP params | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# check if there's a valid submission | ||
submission = Submission.get_submission_by_group_and_assignment(params[:group_name], | ||
params[:assignment]) | ||
if submission.nil? | ||
# no such submission | ||
render :file => "#{RAILS_ROOT}/public/422.xml", :status => 422 | ||
return | ||
end | ||
# request seems good | ||
test_result = submission.test_results.find_by_filename(params[:filename]) | ||
if !test_result.nil? | ||
# Everything went fine; send file_content | ||
send_data test_result.file_content, :disposition => 'inline', | ||
:filename => test_result.filename | ||
return | ||
end | ||
# The test result in question does not exist | ||
render :file => "#{RAILS_ROOT}/public/404.xml", :status => 404 | ||
return | ||
end | ||
|
||
private | ||
|
||
# Helper method to check for required HTTP parameters | ||
def has_required_http_params?(param_hash) | ||
# Note: The blank? method is a Rails extension. | ||
# Specific keys have to be present, and their values | ||
# must not be blank. | ||
if !param_hash[:filename].blank? && | ||
!param_hash[:assignment].blank? && | ||
!param_hash[:group_name].blank? | ||
return true | ||
else | ||
return false | ||
end | ||
end | ||
|
||
# Helper method to check for required HTTP parameters including the | ||
# file_content parameter | ||
def has_required_http_params_including_file_content?(param_hash) | ||
if has_required_http_params?(param_hash) | ||
if !param_hash[:file_content].blank? | ||
return true | ||
end | ||
end | ||
return false | ||
end | ||
|
||
end # end TestResultsController | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.