Skip to content

Commit

Permalink
Merge pull request #639 from avalonmediasystem/feature/VOV-4172
Browse files Browse the repository at this point in the history
Feature/vov 4172
  • Loading branch information
cjcolvar committed Dec 11, 2015
2 parents 68a6867 + 046d138 commit 9cdc216
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 66 deletions.
2 changes: 1 addition & 1 deletion app/assets/javascripts/avalon_player.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class AvalonPlayer
else
$('.mejs-overlay-loading').show().closest('.mejs-layer').show()
splitUrl = (target_stream.nativeUrl || target.attr('href')).split('?')
uri = "#{splitUrl[0]}.json"
uri = "#{splitUrl[0]}.js"
params = ["content=#{segment}"]
params.push(splitUrl[1]) if splitUrl[1]?
$.getJSON uri, params.join('&'), (data) => @setStreamInfo(data)
Expand Down
10 changes: 7 additions & 3 deletions app/controllers/admin/collections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class Admin::CollectionsController < ApplicationController
# GET /collections
def index
respond_to do |format|
format.json { paginate json: Admin::Collection.all }
format.html { @collections = get_user_collections }
format.json { paginate json: Admin::Collection.all }
end
end

Expand Down Expand Up @@ -80,8 +80,12 @@ def edit

# GET /collections/1/items
def items
mos = paginate Admin::Collection.find(params[:id]).media_objects
render json: mos.collect{|mo| [mo.pid, mo.to_json] }.to_h
begin
mos = paginate Admin::Collection.find(params[:id]).media_objects
render json: mos.collect{|mo| [mo.pid, mo.to_json] }.to_h
rescue ActiveFedora::ObjectNotFoundError
render json: {errors: ["Collection not found for #{params[:id]}"]}, status: 404
end
end

# POST /collections
Expand Down
27 changes: 27 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

class ApplicationController < ActionController::Base
before_filter :store_location
around_action :handle_api_request, if: proc{|c| request.format.json?}

# Adds a few additional behaviors into the application controller
include Blacklight::Controller
Expand Down Expand Up @@ -53,6 +54,32 @@ def store_location
end
end

def handle_api_request
if request[:api_key].present?
token = request[:api_key]
#verify token (and IP)
apiauth = Avalon::Authentication::Config.find {|p| p[:id] == :api }
if apiauth[:tokens].include? token
token_creds = apiauth[:tokens][token]
# user = User.find_by_api(token_creds[:username], token_creds[:email])
user = User.find_by_username(token_creds[:username]) ||
User.find_by_email(token_creds[:email]) ||
User.create(:username => token_creds[:username], :email => token_creds[:email])

sign_in user, event: :authentication
user_session[:json_api_login] = true
user_session[:full_login] = false
else
render json: {errors: ["Permission denied."]}, status: 403
return
end
end
yield
if user_session.present? && !!user_session[:json_api_login]
sign_out current_user
end
end

def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
Expand Down
27 changes: 14 additions & 13 deletions app/controllers/media_objects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,10 @@ def create
def json_update
begin
@mediaobject = MediaObject.find(params[:id])
update_mediaobject
rescue ActiveFedora::ObjectNotFoundError
render json: {errors: ["Mediaobject not found for #{params[:id]}"]}, status: 404
return
end
update_mediaobject
end

def update_mediaobject
Expand Down Expand Up @@ -156,29 +155,31 @@ def custom_update

def index
respond_to do |format|
format.json { paginate json: MediaObject.all }
format.json {
paginate json: MediaObject.all
}
end
end

def show
respond_to do |format|
format.html do
if (not @masterFiles.empty? and @currentStream.blank?) then
authorize! :read, @mediaobject
if (not @masterFiles.empty? and @currentStream.blank?) then
redirect_to media_object_path(@mediaobject.pid), flash: { notice: 'That stream was not recognized. Defaulting to the first available stream for the resource' }
else
render
end
end
format.js do
render json: @currentStreamInfo
end
format.json do
if params.has_key? :content #switchStream sends :content to specify which stream and expects currentStreamInfo in response
render json: @currentStreamInfo
else
begin
render json: MediaObject.find(params[:id]).to_json
rescue ActiveFedora::ObjectNotFoundError
render json: {errors: ["Media Object not found for #{params[:id]}"]}, status: 404
end
end
begin
render json: MediaObject.find(params[:id]).to_json
rescue ActiveFedora::ObjectNotFoundError
render json: {errors: ["Media Object not found for #{params[:id]}"]}, status: 404
end
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/vocabulary_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ class VocabularyController < ApplicationController
def index
render json: Avalon::ControlledVocabulary.vocabulary
end

def show
render json: Avalon::ControlledVocabulary.vocabulary[params[:id].to_sym]
end

def update
unless params[:entry].present?
render json: {errors: ["No update value sent"]}, status: 422 and return
end

v = Avalon::ControlledVocabulary.vocabulary
v[params[:id].to_sym] |= Array(params[:entry])
result = Avalon::ControlledVocabulary.vocabulary = v
Expand Down
24 changes: 24 additions & 0 deletions app/models/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,23 @@ def custom_permissions(user=nil, session=nil)
can :share, MediaObject
end

if is_api_request?
can :manage, MediaObject
can :json_index, MediaObject
can :json_show, MediaObject
can :json_create, MediaObject
can :json_update, MediaObject
can :manage, Admin::Collection
can :json_index, Admin::Collection
can :json_show, Admin::Collection
can :json_create, Admin::Collection
can :json_update, Admin::Collection
can :manage, Avalon::ControlledVocabulary
can :json_index, Avalon::ControlledVocabulary
can :json_show, Avalon::ControlledVocabulary
can :json_update, Avalon::ControlledVocabulary
end

cannot :update, MediaObject do |mediaobject|
(not full_login?) || (!is_member_of?(mediaobject.collection)) ||
( mediaobject.published? && !@user.in?(mediaobject.collection.managers) )
Expand Down Expand Up @@ -156,4 +173,11 @@ def full_login?
@full_login = ( @session.present? and @session.has_key? :full_login ) ? @session[:full_login] : true
@full_login
end

def is_api_request?
@json_api_login ||= !!@session[:json_api_login] if @session.present?
@json_api_login ||= false
@json_api_login
end

end
6 changes: 6 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ def self.find_for_lti(auth_hash, signed_in_resource=nil)
User.create(:username => auth_hash.uid, :email => auth_hash.info.email)
end

# def self.find_for_api(username, email, signed_in_resource=nil)
# User.find_by_username(username) ||
# User.find_by_email(email) ||
# User.create(:username => username, :email => email)
# end

def self.autocomplete(query)
self.where("username LIKE :q OR email LIKE :q", q: "%#{query}%").collect { |user|
{ id: user.user_key, display: user.user_key }
Expand Down
7 changes: 7 additions & 0 deletions config/authentication.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
:fields:
- :email

#- :name: Avalon JSON Api
# :id: :api
# :tokens:
# token:
# :username: system_account_name
# :email: system@example.edu
#
#- :name: Avalon Lti OAuth
# :provider: :lti
# :hidden: true
Expand Down
7 changes: 4 additions & 3 deletions config/initializers/authn_providers.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module Avalon
module Authentication
Providers = YAML.load(File.read(File.expand_path('../../authentication.yml',__FILE__)))
VisibleProviders = Providers.reject {|provider| provider[:hidden]}
module Authentication
Config = YAML.load(File.read(File.expand_path('../../authentication.yml',__FILE__)))
Providers = Config.reject {|provider| provider[:provider].blank? }
VisibleProviders = Providers.reject {|provider| provider[:hidden]}
HiddenProviders = Providers - VisibleProviders
end
end
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

resources :vocabulary, except: [:create, :destroy, :new, :edit]

resources :media_objects, except: [:update] do
resources :media_objects, except: [:create, :update] do
member do
put :update, action: :update, defaults: { format: 'html' }, constraints: { format: 'html' }
put :update, action: :json_update, constraints: { format: 'json' }
Expand All @@ -47,6 +47,7 @@
get :confirm_remove
end
collection do
post :create, action: :create, constraints: { format: 'json' }
get :confirm_remove
put :update_status
# 'delete' has special signifigance so use 'remove' for now
Expand Down
6 changes: 6 additions & 0 deletions spec/config/authentication.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@
:params:
:oauth_credentials:
key: 'secret'
- :name: Avalon JSON Api
:id: :api
:tokens:
'secret_token':
:username: system_account_name
:email: system@example.edu
45 changes: 28 additions & 17 deletions spec/controllers/collections_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,13 @@
let!(:collection) { FactoryGirl.create(:collection) }
subject(:json) { JSON.parse(response.body) }

before do
login_as(:administrator)
get 'index', format:'json'
it "should return 403 if bad token passed" do
get 'index', format:'json', api_key:'badtoken'
expect(response.status).to eq(403)
end

it "should return list of collections" do
get 'index', format:'json', api_key:'secret_token'
expect(json.count).to eq(1)
expect(json.first['id']).to eq(collection.pid)
expect(json.first['name']).to eq(collection.name)
Expand Down Expand Up @@ -158,12 +159,12 @@
context "with json format" do
subject(:json) { JSON.parse(response.body) }

before do
login_as(:administrator)
get 'show', id: collection.pid, format:'json'
it "should return 403 if bad token passed" do
get 'show', id: collection.pid, format:'json', api_key:'badtoken'
expect(response.status).to eq(403)
end

it "should return json for specific collection" do
get 'show', id: collection.pid, format:'json', api_key:'secret_token'
expect(json['id']).to eq(collection.pid)
expect(json['name']).to eq(collection.name)
expect(json['unit']).to eq(collection.unit)
Expand All @@ -188,10 +189,14 @@

describe "#items" do
let!(:collection) { FactoryGirl.create(:collection, items: 2) }
it "should return 403 if bad token passed" do
get 'items', id: collection.pid, format:'json', api_key:'badtoken'
expect(response.status).to eq(403)
end

it "should return json for specific collection's media objects" do
login_as(:administrator)
get 'items', id: collection.pid, format: 'json'
get 'items', id: collection.pid, format: 'json', api_key:'secret_token'
expect(JSON.parse(response.body)).to include(collection.media_objects[0].pid,collection.media_objects[1].pid)
#TODO add check that mediaobject is serialized to json properly
end
Expand All @@ -200,20 +205,24 @@

describe "#create" do
let!(:collection) { FactoryGirl.build(:collection) }
before(:each) { login_as(:administrator) } #Login as admin so there will be at least one administrator to get an email
it "should return 403 if bad token passed" do
post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}, api_key:'badtoken'
expect(response.status).to eq(403)
end
it "should notify administrators" do
login_as(:administrator) #otherwise, there are no administrators to mail
mock_delay = double('mock_delay').as_null_object
allow(NotificationsMailer).to receive(:delay).and_return(mock_delay)
expect(mock_delay).to receive(:new_collection)
post 'create', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}
post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}, api_key:'secret_token'
end
it "should create a new collection" do
post 'create', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}
post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}, api_key:'secret_token'
expect(JSON.parse(response.body)['id'].class).to eq String
expect(JSON.parse(response.body)).not_to include('errors')
end
it "should return 422 if collection creation failed" do
post 'create', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit}
post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit}, api_key:'secret_token'
expect(response.status).to eq(422)
expect(JSON.parse(response.body)).to include('errors')
expect(JSON.parse(response.body)["errors"].class).to eq Array
Expand All @@ -223,10 +232,8 @@
end

describe "#update" do
before(:each) do
login_as(:administrator)
end
it "should notify administrators if name changed" do
login_as(:administrator) #otherwise, there are no administrators to mail
mock_delay = double('mock_delay').as_null_object
allow(NotificationsMailer).to receive(:delay).and_return(mock_delay)
expect(mock_delay).to receive(:update_collection)
Expand All @@ -237,17 +244,21 @@
context "update REST API" do
let!(:collection) { FactoryGirl.create(:collection)}

it "should return 403 if bad token passed" do
put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}, api_key:'secret_token', api_key:'badtoken'
expect(response.status).to eq(403)
end
it "should update a collection via API" do
old_description = collection.description
put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}
put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}, api_key:'secret_token'
expect(JSON.parse(response.body)['id'].class).to eq String
expect(JSON.parse(response.body)).not_to include('errors')
collection.reload
expect(collection.description).to eq old_description+'new'
end
it "should return 422 if collection update via API failed" do
allow_any_instance_of(Admin::Collection).to receive(:save).and_return false
put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}
put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}, api_key:'secret_token'
expect(response.status).to eq(422)
expect(JSON.parse(response.body)).to include('errors')
expect(JSON.parse(response.body)["errors"].class).to eq Array
Expand Down

0 comments on commit 9cdc216

Please sign in to comment.