diff --git a/.github/workflows/ams-ci.yml b/.github/workflows/ams-ci.yml
new file mode 100644
index 000000000..5154297d5
--- /dev/null
+++ b/.github/workflows/ams-ci.yml
@@ -0,0 +1,50 @@
+name: CI RSpec Tests
+
+on: [push, pull_request]
+
+jobs:
+ tests:
+ name: CI
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Install Redis
+ run: sudo apt-get install -y redis-tools redis-server
+
+ - name: Install libcurl4-openssl-dev for Curb Gem
+ run: sudo apt-get install libcurl4-openssl-dev
+
+ - name: Setup Ruby and Install RubyGems
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: 2.5.3
+ bundler-cache: true
+
+ - name: Install JDK
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+
+ - name: Install Node
+ shell: bash -l -eo pipefail {0}
+ run: nvm install 12.9.0
+
+ - name: Install Chrome Browser
+ run: google-chrome-stable --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 http://localhost &
+
+ - name: Prepare Test Environment
+ run: |
+ cp config/travis/solr_wrapper_test.yml config/solr_wrapper_test.yml
+ cp config/travis/fcrepo_wrapper_test.yml config/fcrepo_wrapper_test.yml
+ export DISPLAY=:99.0
+ RAILS_ENV=test bundle exec rake db:environment:set db:create db:migrate --trace
+ RAILS_ENV=test npm install yarn
+ RAILS_ENV=test yarn --ignore-engines install
+ RAILS_ENV=test bundle exec rake webpacker:compile
+
+ - name: Run Rspec specs using CI config
+ run: bundle exec rake ci
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b531be476..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-dist: xenial
-language: ruby
-services:
-- redis-server
-- xvfb
-jdk:
-- openjdk11
-rvm:
-- 2.5.3
-addons:
- chrome: stable
-cache:
- bundler: true
- directories:
- - dep_cache
-before_install:
-- google-chrome-stable --headless --disable-gpu --no-sandbox --remote-debugging-port=9222
- http://localhost &
-- mkdir -p dep_cache
-- ls -l dep_cache
-- cp config/travis/solr_wrapper_test.yml config/solr_wrapper_test.yml
-- cp config/travis/fcrepo_wrapper_test.yml config/fcrepo_wrapper_test.yml
-- gem update bundler
-- gem update --system 3.0.6
-- nvm install 12.9.0
-before_script:
-- export DISPLAY=:99.0
-- RAILS_ENV=test bundle exec rake db:environment:set db:create db:migrate --trace
-- RAILS_ENV=test npm install yarn
-- RAILS_ENV=test yarn --ignore-engines install
-- RAILS_ENV=test bundle exec rake webpacker:compile
-script:
-- bundle exec rake ci
-deploy:
-- provider: codedeploy
- access_key_id: AKIAR3SRUQECDKWELTGU
- secret_access_key:
- secure: "vAmLUYYon/glBfFog8+xyChUMidy+5GciyNSfuNpkF+dWH+r5FYmMpA9hbfcTZhNrM9XSZxjf+heVltkz/nTNwb+U7Q35e1k1KR4NO/juf8+VNk+jwK9oQESoSeEU+Pplkl7sUCCZikqPO07aYtIPzCJy8pt2hUsA9EzxPny6vWPSZiGxghxcCqZIHmuiJJFg39Pnl8P4R8MM3EqGr3qExCEtapO4ca4s+JVr8dvhiJCUac7e0rWHSTTzGh1qTyOk1d4T6tU4edJZXc8BFzsZ4O/GETzGfA8bYiaHfizAQNcwpcQ9cP66k+XQkQ+CYRUlHlNbq6JGItkJTUGkY/MHhs3jIy58iWKVEg1dyPsOsjWs6aL4UA52Uo/uV3TPsB/GHbC52gVonzz7pqNeNPoS46RHO2ekqlerkeE488uuM0fGwamQ3JhfDwKBklZcGjzo/sxFD8IKEAieGikcuO3fYIhijhuCBUuOyGt/bCMfMh3rStKGeDRVfzCFHi+WNQY1FtSqqh6P+PNfrR0Y5j2cwKF/6cUFN0iFMZ+LWeEmduTtRDaG1tCyZ1UKRetG69zpWgVn4tmMrVdDOTyyHoE2BrD8D4qft1+690Lvji+6y5g3vqM9sl1aPZO4t21BxKSwwcNLmS/fZo114YpU9Eu9xA3ly7h36OYaei5gh1Z4jc="
- revision_type: github
- application: ams-production-restored
- deployment_group: ams-production-restored-DG
- region: us-east-1
- on:
- branch: main
- ruby: 2.5.3
-- provider: codedeploy
- access_key_id: AKIAR3SRUQECDKWELTGU
- secret_access_key:
- secure: "vAmLUYYon/glBfFog8+xyChUMidy+5GciyNSfuNpkF+dWH+r5FYmMpA9hbfcTZhNrM9XSZxjf+heVltkz/nTNwb+U7Q35e1k1KR4NO/juf8+VNk+jwK9oQESoSeEU+Pplkl7sUCCZikqPO07aYtIPzCJy8pt2hUsA9EzxPny6vWPSZiGxghxcCqZIHmuiJJFg39Pnl8P4R8MM3EqGr3qExCEtapO4ca4s+JVr8dvhiJCUac7e0rWHSTTzGh1qTyOk1d4T6tU4edJZXc8BFzsZ4O/GETzGfA8bYiaHfizAQNcwpcQ9cP66k+XQkQ+CYRUlHlNbq6JGItkJTUGkY/MHhs3jIy58iWKVEg1dyPsOsjWs6aL4UA52Uo/uV3TPsB/GHbC52gVonzz7pqNeNPoS46RHO2ekqlerkeE488uuM0fGwamQ3JhfDwKBklZcGjzo/sxFD8IKEAieGikcuO3fYIhijhuCBUuOyGt/bCMfMh3rStKGeDRVfzCFHi+WNQY1FtSqqh6P+PNfrR0Y5j2cwKF/6cUFN0iFMZ+LWeEmduTtRDaG1tCyZ1UKRetG69zpWgVn4tmMrVdDOTyyHoE2BrD8D4qft1+690Lvji+6y5g3vqM9sl1aPZO4t21BxKSwwcNLmS/fZo114YpU9Eu9xA3ly7h36OYaei5gh1Z4jc="
- revision_type: github
- application: ams-demo-restore1
- deployment_group: ams-demo-restore1-DG
- region: us-east-1
- on:
- branch: develop
- ruby: 2.5.3
-env:
- matrix:
- secure: bhFHxEHJJKvXc1rXvhx6ip9anTD9vEZSUO+rkXDN3M2HOV3wco2Dt8HH+7gy1fS3A8l/5+VB1LQ0vwRzykQlGARuGIFFd9y9VaPsdAdjqJbTeD6Neb4SHFu7pOEbhfCfdkU/wOLTn1HQ46bl0u33E3fFeVLRyN1vyIuvYW3o9ZHpfhni8enGC9UbQt65DHUVSUCgynutKIWK/lIiiIzxrOhySjQN3u05/W38o1nwsQLi3pWjj20SLD7U42VPK72TzIqkfs4LPcOSb9we/EMdWhIcrfqRZrC/bbVXB/56Un4ZUF/83y0dQJoglcHB7S+rRCGSx48b2ZtojG6B2vdJ96fNuDePf1YhTkolt9VxDL70AZdIiszADSPYJY4OgI4bUInl2BQvxueXQqoZjLkXSxLdHTD5ImZwfYioV3qgmdWXKdmxc6+MRlOznKXE1oHJqCtnwFC47BN4gq7VZoQHiQdpx4BMOWF13b6qGtO8pJK59bGDQPSO+eskBpZfghad3aMJ9c8+FESkDN9la8HXxlwyZDVNpysVLFFZqFcQlWQ/NG3r/e/NhmAfs3uyqi+bC4dsgg4MInfNQnzErshpfbvTXFJ29cKYnWCY9cWD0zLau2VehXHUxdaBoPaBONM8K73i3aqv/2YMZwSYAy/1egDslliOdyDb9ACKkKt/g9w=
diff --git a/Gemfile b/Gemfile
index 4fc7f06ba..93a9a0681 100644
--- a/Gemfile
+++ b/Gemfile
@@ -92,7 +92,8 @@ gem 'bootstrap-multiselect-rails'
gem 'hyrax-batch_ingest', git: 'https://github.com/samvera-labs/hyrax-batch_ingest'
gem 'pbcore', '~> 0.3.0'
gem 'curb'
-gem 'sony_ci_api', '~> 0.2.1'
+# gem 'sony_ci_api', '~> 0.2.1'
+gem 'sony_ci_api', github: 'WGBH-MLA/sony_ci_api_rewrite', branch: 'v0.1'
# gem 'hyrax-iiif_av', '>= 0.2.0'
# gem 'hyrax-iiif_av', github: 'samvera-labs/hyrax-iiif_av', branch: 'hyrax_master'
gem 'webpacker'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5fbeb3b30..2333f74c8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -25,6 +25,15 @@ GIT
rails (>= 5.1.6)
rdf (>= 2.0.2, < 4.0)
simple_form
+GIT
+ remote: https://github.com/WGBH-MLA/sony_ci_api_rewrite.git
+ revision: f98576c7060e11cc50da0f67cf4642d2b4372517
+ branch: v0.1
+ specs:
+ sony_ci_api (0.1.0)
+ activesupport
+ faraday (~> 0.12)
+ faraday_middleware
GIT
remote: https://github.com/samvera-labs/hyrax-batch_ingest
@@ -1001,7 +1010,7 @@ GEM
temple (0.8.2)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
- thor (0.20.3)
+ thor (1.1.0)
thread_safe (0.3.6)
tilt (2.0.10)
tinymce-rails (4.9.11)
@@ -1114,7 +1123,8 @@ DEPENDENCIES
sidekiq
simple_form (= 5.0.0)
solr_wrapper (~> 2.1)
- sony_ci_api (~> 0.2.1)
+ sony_ci_api!
+ sqlite3 (= 1.3.13)
turbolinks (~> 5)
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index d8217c3be..30e0244cf 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -10,7 +10,6 @@
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
-//=
//= require turbolinks
//
// Required by Blacklight
diff --git a/app/assets/javascripts/sony_ci/find_media.coffee b/app/assets/javascripts/sony_ci/find_media.coffee
new file mode 100644
index 000000000..eaec78297
--- /dev/null
+++ b/app/assets/javascripts/sony_ci/find_media.coffee
@@ -0,0 +1,103 @@
+class FindSonyCiMediaBehavior
+ # Select for the search button
+ searchButtonSelector: '#find_sony_ci_media #search'
+ # Selector for the div that displays the feedback messages.
+ feedbackSelector: '#find_sony_ci_media #feedback'
+ # Selector for the text inputs that have the Sony Ci IDs.
+ sonyCiIdInputSelector: 'input.asset_sonyci_id'
+ # Selector for the button to add new Sony Ci IDs
+ addNewSonyCiIdButtonSelector: '.form-group.asset_sonyci_id button.add'
+
+ constructor: (@query) ->
+
+ # searchHandler - search Sony Ci for records matching the query and provide
+ # feedback to the user.
+ searchHandler: (event) =>
+ event.preventDefault()
+ @giveFeedback('searching...')
+ $.ajax(
+ context: this,
+ url: "/sony_ci/api/find_media",
+ data: { query: @query }
+ ).done ( response ) ->
+ @giveFeedback(response.length + ' records found')
+ if response.length > 0
+ @addFoundRecords(response)
+
+ # fetchFilenameHandler - fetches the filename from Sony Ci given a Sony Ci ID
+ # from an input field.
+ fetchFilenameHandler: ->
+ $.ajax(
+ url: "/sony_ci/api/get_filename",
+ context: this,
+ data: { sony_ci_id: $(this).val() }
+ ).done(( response ) ->
+ $(this).parent().find('.sony_ci_filename').text(response['name'])
+ ).fail(( response ) ->
+ $(this).parent().find('.sony_ci_filename').text("Could not find Sony Ci record")
+ )
+
+ # Adds a message to give the user feedback on what's happening.
+ # The element is hidden at first, so set the text and reveal it.
+ giveFeedback: (msg) =>
+ $(@feedbackSelector).text(msg).show()
+
+ addFoundRecords: (records) =>
+ # Map the sonyci_id text inputs to their values.
+ existingSonyCiIds = $(@sonyCiIdInputSelector).map (_, element) ->
+ $(element).val()
+
+ # Map the found records to just the Sony Ci IDs.
+ # This is not a jQuery.map function, so the index is the 2nd arg instead of
+ # the first, like in the map function above.
+ foundSonyCiIds = records.map (record, _) ->
+ record['id']
+
+ # Subtract the existing Sony Ci Ids from the found Sony Ci IDs.
+ newSonyCiIds = $(foundSonyCiIds).not(existingSonyCiIds).get();
+
+ # For each of the new found Sony Ci IDs...
+ newSonyCiIds.forEach (sonyCiId, index) =>
+
+ # Insert the found Sony Ci ID into the last text input and trigger the \
+ # change() event, because just setting val(x) won't do it.
+ $(@sonyCiIdInputSelector).last().val(sonyCiId).change()
+
+ # If we have more Sony Ci IDs to add
+ if newSonyCiIds.length > index
+ # Add another Sony Ci ID field it by clicking the "Add another..."
+ # button.
+ $(@addNewSonyCiIdButtonSelector).click()
+
+ # Hyrax will simply copy and append the last element, but we don't want
+ # values for Sony Ci ID or Filename there, so clear them out.
+ $(@sonyCiIdInputSelector).last().val('')
+ $('.sony_ci_filename').last().text('')
+
+ # Finally, add the handler to the change() event of the input.
+ $(@sonyCiIdInputSelector).last().change @fetchFilenameHandler
+
+ # apply - Attaches handlers to events.
+ apply: ->
+ # Attach the search handler to the click event of the search button.
+ $(@searchButtonSelector).click @searchHandler
+ # Attach the fetchFilenameHanlder to the change event of the inputs.
+ $(@sonyCiIdInputSelector).change @fetchFilenameHandler
+
+# When the page loads...
+# NOTE: could not get $(document).on('turbolinks:load') to work on initial page
+# load; reverting to $(document).ready, which seems to work more consistently.
+$(document).ready ->
+ # This regex matches the 3rd URL segment which should be the GUID.
+ guid_query_str = window.location.href.match(/concern\/assets\/(.*)\//)[1]
+
+ # Create the behavior object, passing in the GUID as the query string.
+ # NOTE: Sony Ci API has a 20 character limit on it's search terms, so let's
+ # just pass in the last 20 characters, which will be more unique than the 1st
+ # 20 chars due to the common prefix of "cpb-aacip-". Supposedly, Michael Potts
+ # from Sony Ci said that quoted search queries have no such limit, but I could
+ # not get that to work, nor is it mentioned in the Ci API docs anywhere.
+ behavior = new FindSonyCiMediaBehavior(guid_query_str.substr(-20))
+
+ # apply the behavior
+ behavior.apply()
diff --git a/app/controllers/api/assets_controller.rb b/app/controllers/api/assets_controller.rb
new file mode 100644
index 000000000..ae394eb9b
--- /dev/null
+++ b/app/controllers/api/assets_controller.rb
@@ -0,0 +1,38 @@
+module API
+ class AssetsController < APIController
+ # Authenticate user before all actions.
+ # NOTE: For Basic HTTP auth to work:
+ # * the `http_authenticatable` config option for Devise must be set to true
+ # (see config/initializers/devise.rb).
+ # * The Authorization request header must be set to "Basic {cred}" where
+ # {cred} is the base64 encoded username:password.
+ # TODO: Move authn into base APIController class and make modifications so
+ # that the SonyCi::APIController will work with authn, which needs to be
+ # done.
+ before_action do
+ authenticate_user!
+ end
+
+
+ def show
+ respond_to do |format|
+ format.json { render json: pbcore_json }
+ format.xml { render xml: pbcore_xml }
+ end
+ end
+
+ private
+
+ def pbcore_json
+ @pbcore_json ||= Hash.from_xml(pbcore_xml).to_json
+ end
+
+ def pbcore_xml
+ @pbcore_xml ||= solr_doc.export_as_pbcore
+ end
+
+ def solr_doc
+ @solr_doc ||= SolrDocument.find(params[:id])
+ end
+ end
+end
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index 5908a91df..748eba396 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -1,3 +1,15 @@
class APIController < ActionController::API
+ # Gives us respond_to in controller actions which we use to respond with
+ # JSON or PBCore XML.
+ include ActionController::MimeResponds
+
# Common API features here, e.g. auth.
+ rescue_from ActiveFedora::ObjectNotFoundError, with: :not_found
+
+ private
+
+ def not_found(error)
+ # TODO: render errors in the proper format: xml or json.
+ render text: "Not Found", status: 404
+ end
end
diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb
index 8e589819d..832c92366 100644
--- a/app/controllers/media_controller.rb
+++ b/app/controllers/media_controller.rb
@@ -1,32 +1,68 @@
require 'sony_ci_api'
class MediaController < ApplicationController
+
+ rescue_from StandardError, with: :error_response_default
+ rescue_from Blacklight::Exceptions::RecordNotFound, with: :error_response_404
+ rescue_from SonyCiApi::HttpError, with: :error_response_from_sony_ci
+
def show
- if solr_document
- if can? :show, solr_document
- redirect_to download_url
- else
- head :forbidden
- end
- else
- head :not_found
- end
+ head(:forbidden) and return unless can? :show, solr_document
+ head(:not_found) and return unless download_url
+ redirect_to download_url and return
end
private
+ # Returns the download_url (aka the 'location') of the media for the Solr
+ # document's Sony Ci ID
def download_url
- @download_url ||= ci.download(solr_document['sonyci_id_ssim'][(params['part'] || 0).to_i])
+ sony_ci_response['location']
+ end
+
+ # Fetches the response from the actual Sony Ci api.
+ def sony_ci_response
+ @sony_ci_response ||= ci.asset_download(sony_ci_id)
+ end
+
+ # Returns the Sony Ci ID from the list of Sony Ci IDs in the multi-valued
+ # sonyci_id_ssim Solr field; nil if the sonyci_id_ssim field is empty.
+ def sony_ci_id
+ sony_ci_ids = solr_document['sonyci_id_ssim']
+ if sony_ci_ids.present?
+ # `part` defaults to 0 if `params['part']` is nil.
+ part = params['part'].to_i
+ sony_ci_ids[part]
+ end
end
def ci
- credentials = YAML.load(ERB.new(File.read('config/ci.yml')).result)
- @ci ||= SonyCiBasic.new(credentials:credentials)
+ @ci ||= SonyCiApi::Client.new('config/ci.yml')
end
def solr_document
- @solr_document ||= ActiveFedora::Base.find(params['id']).to_solr
- rescue ActiveFedora::ObjectNotFoundError
- nil
+ @solr_document ||= SolrDocument.find(params['id'])
+ end
+
+ ################
+ # Error handling
+ ################
+ def error_response_default(error)
+ log_error error
+ head :internal_server_error
+ end
+
+ def error_response_404(error)
+ log_error error
+ head :not_found
+ end
+
+ def error_response_from_sony_ci(error)
+ log_error error
+ head error.http_status
+ end
+
+ def log_error(error)
+ Rails.logger.error(error.class) { error.message }
end
end
diff --git a/app/controllers/sony_ci/api_controller.rb b/app/controllers/sony_ci/api_controller.rb
new file mode 100644
index 000000000..1b043622d
--- /dev/null
+++ b/app/controllers/sony_ci/api_controller.rb
@@ -0,0 +1,93 @@
+require 'sony_ci_api'
+
+module SonyCi
+ class APIController < ::APIController
+
+ respond_to :json
+
+ # Specify error handlers for different kinds of errors. NOTE: for *all*
+ # endpoints, we *always* want to respond with JSON and an appropriate HTTP
+ # error, regardless of success or error. We *never* want to accidentally
+ # render HTML, or anything other than JSON. This is the contract with
+ # consumers, and if we violate it we'll break JS elsewhere. In order to
+ # guarantee this, each error handler must have additional rescue clauses
+ # to catch any additional exceptions, and render as JSON.
+ rescue_from StandardError, with: :handle_error
+ rescue_from SonyCiApi::Error, with: :handle_sony_ci_api_error
+ rescue_from SonyCiApi::HttpError, with: :handle_sony_ci_api_http_error
+ rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing_error
+
+ # Returns a list of Sony Ci records that result from searching for
+ # params[:query].
+ def find_media
+ result = sony_ci_api.workspace_search(
+ query: permitted_params.require(:query),
+ fields: return_fields,
+ kind: "Asset"
+ )
+ render json: result
+ end
+
+ # Returns a JSON object with the ID and Name of the Sony Ci Record if found
+ # from params[:sony_ci_id]
+ def get_filename
+ sony_ci_id = params.require(:sony_ci_id)
+ result = sony_ci_api.asset(sony_ci_id)
+ render json: { 'sony_ci_id' => sony_ci_id, 'name' => result['name'] }
+ end
+
+ private
+
+ # Memoized accessor for the SonyCiApi::Client instance.
+ def sony_ci_api
+ @sony_ci_api ||= SonyCiApi::Client.new('config/ci.yml')
+ end
+
+ # Sony Ci will return 'id', 'name', and 'kind' plus any other fields
+ # specified in a comma-separated list in :fields param. An empty string
+ # for the :fields param will return all fields. If :fields is nil,
+ # specify 'createdOn' as an additional default field.
+ def return_fields
+ params[:fields] || 'createdOn'
+ end
+
+ def permitted_params
+ params.permit(:query, :fields)
+ end
+
+ # Renders generic JSON error object with 500 status.
+ def render_basic_error(error)
+ render json: { "error" => error.class.to_s, "error_message" => error.message }, status: 500
+ end
+
+ # Default error handler.
+ def handle_error(error)
+ render_basic_error(error)
+ rescue => secondary_error
+ render_basic_error(secondary_error)
+ end
+
+ # Error handler for SonyCiApi::Error errors.
+ # Renders JSON from the error object with a default 500 status.
+ def handle_sony_ci_api_error(sony_ci_api_error)
+ render json: sony_ci_api_error.to_h, status: 500
+ rescue => secondary_error
+ render_basic_error(secondary_error)
+ end
+
+ # Error handler for SonyCiApi::HttpError errors.
+ # Renders JSON from the error object, with a status also from the error
+ # object, which will be something between 400 and 599.
+ def handle_sony_ci_api_http_error(sony_ci_api_http_error)
+ render json: sony_ci_api_http_error.to_h, status: sony_ci_api_http_error.http_status
+ rescue => secondary_error
+ render_basic_error(secondary_error)
+ end
+
+ def handle_parameter_missing_error(parameter_missing_error)
+ render json: { "error" => "Missing Parameter", "error_message" => parameter_missing_error.message }, status: 400
+ rescue => secondary_error
+ render_basic_error(secondary_error)
+ end
+ end
+end
diff --git a/app/controllers/sony_ci/webhook_logs_controller.rb b/app/controllers/sony_ci/webhook_logs_controller.rb
index 11150f06c..91feb296b 100644
--- a/app/controllers/sony_ci/webhook_logs_controller.rb
+++ b/app/controllers/sony_ci/webhook_logs_controller.rb
@@ -1,25 +1,75 @@
class SonyCi::WebhookLogsController < ApplicationController
- before_action :set_sony_ci_webhook_log, only: [:show ]
+
+ before_action(only: :index) do
+ @pagination = Pagination.new(
+ total: SonyCi::WebhookLog.count,
+ page: params.fetch('page', 1),
+ per_page: params.fetch('per_page', 50)
+ )
+ end
# GET /sony_ci/webhook_logs
# GET /sony_ci/webhook_logs.json
def index
- @sony_ci_webhook_logs = SonyCi::WebhookLog.all
+ @presenters = sony_ci_webhook_logs.map do |sony_ci_webhook_log|
+ SonyCi::WebhookLogPresenter.new(sony_ci_webhook_log)
+ end
end
# GET /sony_ci/webhook_logs/1
# GET /sony_ci/webhook_logs/1.json
def show
+ respond_to do |format|
+ format.html do
+ @presenter = SonyCi::WebhookLogPresenter.new(sony_ci_webhook_log)
+ end
+ format.json
+ end
end
private
- # Use callbacks to share common setup or constraints between actions.
- def set_sony_ci_webhook_log
- @sony_ci_webhook_log = SonyCi::WebhookLog.find(params[:id])
+ def sony_ci_webhook_log
+ @sony_ci_webhook_log ||= SonyCi::WebhookLog.find(params[:id])
+ end
+
+ def sony_ci_webhook_logs
+ @sony_ci_webhook_logs ||= SonyCi::WebhookLog.all.order(sort_order).limit(per_page).offset(offset)
+ end
+
+ def sort_order
+ { created_at: :desc }
+ end
+
+ def per_page
+ params.fetch(:per_page, 50).to_i
+ end
+
+ def offset
+ [0, page.to_i - 1].max * per_page
+ end
+
+ def page
+ params.fetch(:page, 1)
end
- # Only allow a list of trusted parameters through.
- def sony_ci_webhook_log_params
- params.fetch(:sony_ci_webhook_log, {})
+ class Pagination
+ attr_reader :total, :page, :per_page
+ def initialize(total:, page: 1, per_page: 50)
+ @total, @page, @per_page = total.to_i, page.to_i, per_page.to_i
+ end
+
+ def showing
+ "#{lower_bound} - #{upper_bound}"
+ end
+
+ private
+
+ def lower_bound
+ ((page - 1) * per_page) + 1
+ end
+
+ def upper_bound
+ [ ( page * per_page ), total ].min
+ end
end
end
diff --git a/app/controllers/sony_ci/webhooks_controller.rb b/app/controllers/sony_ci/webhooks_controller.rb
index b2f44ad73..20f526757 100644
--- a/app/controllers/sony_ci/webhooks_controller.rb
+++ b/app/controllers/sony_ci/webhooks_controller.rb
@@ -1,5 +1,5 @@
module SonyCi
- class WebhooksController < APIController
+ class WebhooksController < ::APIController
after_action :create_webhook_log
rescue_from StandardError do |error|
@@ -16,16 +16,29 @@ class WebhooksController < APIController
end
def save_sony_ci_id
- asset_admin_data_from_sony_ci_filename.update!( sonyci_id: [ sony_ci_id ] )
- render status: 200, json: { message: "success" }
+ asset.admin_data.update!( sonyci_id: [ sony_ci_id ] )
+ # Re-save the Asset to re-index it.
+ # TODO: Is there a faster way to save the Sony Ci ID to the AdminData and
+ # re-index the Asset?
+ asset.save!
+ render status: 200,
+ json: {
+ message: "success",
+ guid: asset.id,
+ sony_ci_id: sony_ci_id
+ }
end
private
- def asset_admin_data_from_sony_ci_filename
- Asset.find(guid_from_sony_ci_filename).admin_data
+ def asset
+ @asset ||= Asset.find(guid_from_sony_ci_filename)
end
+ # Returns the assumed GUID from the Sony Ci Filename.
+ # This assumes a naming convention of {GUID}.ext, where the GUID is the
+ # string before the first dot. If this convention is not followed, then
+ # this feature will not work.
def guid_from_sony_ci_filename
sony_ci_filename.sub(/\..*/, '') unless sony_ci_filename.empty?
end
@@ -44,9 +57,12 @@ def sony_ci_id
def create_webhook_log(error: nil)
webhook_log.response_headers = response.headers.to_h
webhook_log.response_body = response_json
+ webhook_log.response_status = response.status
if error
webhook_log.error = error.class
webhook_log.error_message = error.message
+ else
+ webhook_log.guids = [ guid_from_sony_ci_filename ]
end
webhook_log.save!
end
diff --git a/app/input/sony_ci_id_input.rb b/app/input/sony_ci_id_input.rb
new file mode 100644
index 000000000..0095e1e27
--- /dev/null
+++ b/app/input/sony_ci_id_input.rb
@@ -0,0 +1,16 @@
+class SonyCiIdInput < MultiValueInput
+ def build_field(value, index)
+ super + sony_ci_filename_html(value).to_s.html_safe
+ end
+
+ private
+
+ def sony_ci_filename_html(sony_ci_id)
+ # NOTE: object.model.admin_data will be FALSE if this is a new record so
+ # we need to guard against that.
+ filename = if object.model.admin_data.present?
+ object.model.admin_data.sonyci_records&.fetch(sony_ci_id, nil)&.fetch('name', nil)
+ end
+ "
Filename: #{filename}
"
+ end
+end
diff --git a/app/models/admin_data.rb b/app/models/admin_data.rb
index 08f26e92b..d72d88142 100644
--- a/app/models/admin_data.rb
+++ b/app/models/admin_data.rb
@@ -58,4 +58,23 @@ def asset(refresh: false)
@asset_error = error
nil
end
+
+ # Returns a hash of Sony Ci records fetched from the Sony Ci API, keyed
+ # by the Sony Ci ID.
+ def sonyci_records
+ # NOTE: sonyci_id (although singular) is actually a serialized array.
+ # We do hit the API per sonyci_id here, but over 99% of the time, there will
+ # be only one, and when there's more, there are not a whole bunch.
+ @sonyci_records ||= {}.tap do |hash|
+ sonyci_id.each { |id| hash[id] = sony_ci_api.asset(id) }
+ end
+ rescue => e
+ Rails.logger.error "Could not retrieve records from Sony Ci API.\n " \
+ "#{e.class}: #{e.message}"
+ nil
+ end
+
+ def sony_ci_api
+ @sony_ci_api ||= SonyCiApi::Client.new('config/ci.yml')
+ end
end
diff --git a/app/models/asset.rb b/app/models/asset.rb
index 39b5d94a4..78175b8bc 100644
--- a/app/models/asset.rb
+++ b/app/models/asset.rb
@@ -298,6 +298,10 @@ def canonical_meta_tag
canonical_meta_tag ||= find_annotation_attribute("canonical_meta_tag")
end
+ def proxy_start_time
+ proxy_start_time ||= find_annotation_attribute("proxy_start_time")
+ end
+
def find_annotation_attribute(attribute)
if admin_data.annotations.select { |a| a.annotation_type == attribute }.present?
return admin_data.annotations.select { |a| a.annotation_type == attribute }.map(&:value)
diff --git a/app/models/solr_document.rb b/app/models/solr_document.rb
index a5565e111..79d44701b 100644
--- a/app/models/solr_document.rb
+++ b/app/models/solr_document.rb
@@ -520,6 +520,10 @@ def md5
self[Solrizer.solr_name('md5', :symbol)]
end
+ def proxy_start_time
+ self[Solrizer.solr_name('proxy_start_time','ssim')]
+ end
+
def all_members(only: [], exclude: [])
# Fetch members recursively and memoize. Subtract self from the list.
@all_members ||= SolrDocument.get_members(self) - [ self ]
diff --git a/app/models/sony_ci/webhook_log.rb b/app/models/sony_ci/webhook_log.rb
index 8a401d8df..dcc8f48dc 100644
--- a/app/models/sony_ci/webhook_log.rb
+++ b/app/models/sony_ci/webhook_log.rb
@@ -1,6 +1,11 @@
class SonyCi::WebhookLog < ApplicationRecord
- serialize :request_header, JSON
+ serialize :request_headers, JSON
serialize :request_body, JSON
- serialize :response_header, JSON
+ serialize :response_headers, JSON
serialize :response_body, JSON
+ serialize :guids, Array
+
+ validates :url, presence: true
+ validates :action, presence: true
+ validates :response_status, inclusion: { in: 200..599 }
end
diff --git a/app/presenters/sony_ci/webhook_log_presenter.rb b/app/presenters/sony_ci/webhook_log_presenter.rb
new file mode 100644
index 000000000..72e2e8f3d
--- /dev/null
+++ b/app/presenters/sony_ci/webhook_log_presenter.rb
@@ -0,0 +1,69 @@
+module SonyCi
+ class WebhookLogPresenter
+
+ DATETIME_FORMAT = '%m/%d/%Y %I:%M:%S %P'
+
+ attr_reader :webhook_log
+
+ delegate :id, :url, :error, :error_message, :status, :guids,
+ to: :webhook_log
+
+ def initialize(webhook_log)
+ raise ArgumentError, "expected first parameter to be a " \
+ "SonyCi::WebhookLog but #{webhook_log.class} was " \
+ "given" unless webhook_log.is_a? SonyCi::WebhookLog
+ @webhook_log = webhook_log
+ end
+
+ def status
+ webhook_log.error ? "Fail" : "Success"
+ end
+
+ def created_at
+ webhook_log.created_at.strftime(DATETIME_FORMAT)
+ end
+
+ def action
+ WebhookLogPresenter.actions[webhook_log.action] || "None"
+ end
+
+ def request_headers
+ return "None" unless webhook_log.request_headers
+ http_headers(webhook_log.request_headers)
+ end
+
+ def request_body
+ return "None" unless webhook_log.request_body
+ JSON.pretty_generate(webhook_log.request_body)
+ end
+
+ def response_headers
+ return "None" unless webhook_log.response_headers
+ http_headers(webhook_log.response_headers)
+ end
+
+ def response_body
+ return "None" unless webhook_log.response_body
+ JSON.pretty_generate(webhook_log.response_body)
+ end
+
+ private
+
+ def http_headers(headers_hash)
+ headers_hash.map { |header, val|
+ "#{header}: #{val}"
+ }.join("\n")
+ end
+
+
+ class << self
+ # Returns a mapping of recognized actions from SonyCi::WebhookController
+ # to display text.
+ def actions
+ {
+ 'save_sony_ci_id' => "Link Asset to Sony Ci Media"
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/ams/media_download/media_download_service.rb b/app/services/ams/media_download/media_download_service.rb
index 93c82189e..a9b8a4f8d 100644
--- a/app/services/ams/media_download/media_download_service.rb
+++ b/app/services/ams/media_download/media_download_service.rb
@@ -34,8 +34,7 @@ def self.cleanup_temp_file(temp_file: nil)
private
def ci
- credentials = YAML.load(ERB.new(File.read('config/ci.yml')).result)
- @ci ||= SonyCiBasic.new(credentials:credentials)
+ @ci ||= SonyCiApi::Client.new('config/ci.yml')
end
def process_download
@@ -82,7 +81,7 @@ def delete_media_files(array_of_files)
end
def get_sonyci_file_location(sonyci_id)
- ci.download(sonyci_id)
+ ci.asset_download(sonyci_id)['location']
end
def parse_sonyci_file_name(location)
diff --git a/app/views/records/edit_fields/_default.html.erb b/app/views/records/edit_fields/_default.html.erb
index 0137cbc34..3e4ee560e 100644
--- a/app/views/records/edit_fields/_default.html.erb
+++ b/app/views/records/edit_fields/_default.html.erb
@@ -4,4 +4,4 @@
<%= f.input key, required: f.object.required?(key), as: :hidden %>
<% else %>
<%= f.input key, required: f.object.required?(key), disabled:f.object.respond_to?(:disabled?) && f.object.disabled?(key), readonly:f.object.respond_to?(:readonly?) && f.object.readonly?(key) %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/app/views/records/edit_fields/_md5.html.erb b/app/views/records/edit_fields/_md5.html.erb
index e5f4febeb..0ed671be8 100644
--- a/app/views/records/edit_fields/_md5.html.erb
+++ b/app/views/records/edit_fields/_md5.html.erb
@@ -1,2 +1 @@
-
<%= f.input :md5, label: 'md5' ,required: f.object.required?(key), disabled:f.object.respond_to?(:disabled?) && f.object.disabled?(key) %>
diff --git a/app/views/records/edit_fields/_sonyci_id.html.erb b/app/views/records/edit_fields/_sonyci_id.html.erb
new file mode 100644
index 000000000..fc2eb7b08
--- /dev/null
+++ b/app/views/records/edit_fields/_sonyci_id.html.erb
@@ -0,0 +1,17 @@
+<%= javascript_include_tag "sony_ci/find_media" %>
+<% asset_id = f.object.model.id %>
+<% if asset_id %>
+
+<% end %>
+
+<%=
+ f.input 'sonyci_id', as: :sony_ci_id,
+ input_html: { class: 'form-control' },
+ wrapper_html: { class: 'multi_value' },
+ disabled: f.object.respond_to?(:disabled?) && f.object.disabled?(key),
+ readonly: f.object.respond_to?(:readonly?) && f.object.readonly?(key),
+ required: f.object.required?(key)
+%>
diff --git a/app/views/sony_ci/webhook_logs/index.html.erb b/app/views/sony_ci/webhook_logs/index.html.erb
index 77fc3af58..646f20fab 100644
--- a/app/views/sony_ci/webhook_logs/index.html.erb
+++ b/app/views/sony_ci/webhook_logs/index.html.erb
@@ -1,18 +1,74 @@
+
+
<%= notice %>
Sony Ci Webhook Logs
-
+<% if @pagination %>
+
+
Showing <%= @pagination.showing %> of <%= @pagination.total %>
+
Pages:
+ <% ( @pagination.total / @pagination.per_page + 1 ).times do |page| %>
+ <%= link_to (page + 1).to_i, sony_ci_webhook_logs_path( page: page + 1, per_page: @pagination.per_page ) %>
+ <% end %>
+
+
+<% end %>
+
+
- |
+ Date/Time |
+ Action |
+ Status |
+ GUIDs |
- <% @sony_ci_webhook_logs.each do |sony_ci_webhook_log| %>
-
- <%= link_to 'Show', sony_ci_webhook_log %> |
+ <% @presenters.each_with_index do |presenter, i| %>
+
">
+ <%= presenter.created_at %> |
+
+ <%= link_to presenter.action, sony_ci_webhook_log_path(presenter.id) %>
+ |
+ <%= presenter.status %> |
+
+
+ <% presenter.guids.each_with_index do |guid, i| %>
+ - <%= link_to guid, hyrax_asset_path(guid) %>
+ <% end %>
+
+ |
<% end %>
diff --git a/app/views/sony_ci/webhook_logs/show.html.erb b/app/views/sony_ci/webhook_logs/show.html.erb
index 8d82107d2..5c461ca22 100644
--- a/app/views/sony_ci/webhook_logs/show.html.erb
+++ b/app/views/sony_ci/webhook_logs/show.html.erb
@@ -1,3 +1,51 @@
+
+
<%= notice %>
-<%= link_to 'Back', sony_ci_webhook_logs_path %>
+
+ - Action
+ - <%= @presenter.action %>
+
+ - Status
+ - <%= @presenter.status %>
+
+ - Date
+ - <%= @presenter.created_at %>
+
+ <% if @presenter.error %>
+ - Error
+ - <%= @presenter.error || "None" %>
+
+ - Error Message
+ - <%= @presenter.error_message || "None" %>
+ <% end %>
+
+ - URL
+ - <%= @presenter.url || "Unavailable" %>
+
+ - Request Headers
+ -
+
<%= @presenter.request_headers || "None" %>
+
+
+ - Request Body
+ -
+
<%= @presenter.request_body || "None" %>
+
+
+ - Response Headers
+ -
+
<%= @presenter.response_headers || "None" %>
+
+
+ - Response Body
+ -
+
<%= @presenter.response_body || "None" %>
+
+
+
+<%= link_to 'View All', sony_ci_webhook_logs_path %>
diff --git a/config/authorities/annotation_types.yml b/config/authorities/annotation_types.yml
index c2d825c2f..660cbf75d 100644
--- a/config/authorities/annotation_types.yml
+++ b/config/authorities/annotation_types.yml
@@ -36,4 +36,6 @@ terms:
- id: transcript_url
term: Transcript URL
- id: transcript_source
- term: Transcript Source
\ No newline at end of file
+ term: Transcript Source
+ - id: proxy_start_time
+ term: Proxy Start Time
diff --git a/config/authorities/format.yml b/config/authorities/format.yml
index bf4efe45e..069015896 100644
--- a/config/authorities/format.yml
+++ b/config/authorities/format.yml
@@ -138,7 +138,7 @@ terms:
- id: 22mm film
term: 22mm film
- id: 28mm film
- - term: 28mm film
+ term: 28mm film
- id: 35mm film
term: 35mm film
- id: 70mm film
@@ -181,8 +181,8 @@ terms:
Betamax: Super
- id: Blu-ray disc
term: Blu-ray disc
- - id: Cartivision
- term: Cartivision
+ - id: Cartrivision
+ term: Cartrivision
- id: CD
term: CD
- id: D1
diff --git a/config/authorities/topics.yml b/config/authorities/topics.yml
index 32701ed1c..d33fa24b3 100644
--- a/config/authorities/topics.yml
+++ b/config/authorities/topics.yml
@@ -69,8 +69,8 @@ terms:
term: News
- id: Parenting
term: Parenting
- - id: Perfoming Arts
- term: Perfoming Arts
+ - id: Performing Arts
+ term: Performing Arts
- id: Philosophy
term: Philosophy
- id: Politics and Government
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
index b3e9d6dad..980166766 100644
--- a/config/initializers/assets.rb
+++ b/config/initializers/assets.rb
@@ -12,4 +12,4 @@
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
-Rails.application.config.assets.precompile += %w( work_actions.js work_show/work_show.css )
+Rails.application.config.assets.precompile += %w( work_actions.js work_show/work_show.css sony_ci/find_media.coffee )
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 3ada7e643..994ee151f 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -68,7 +68,7 @@
# given strategies, for example, `config.http_authenticatable = [:database]` will
# enable it only for database authentication. The supported strategies are:
# :database = Support basic authentication with authentication key + password
- # config.http_authenticatable = false
+ config.http_authenticatable = true
# If 401 status code should be returned for AJAX requests. True by default.
# config.http_authenticatable_on_xhr = true
diff --git a/config/routes.rb b/config/routes.rb
index 9837e03aa..f148daa61 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,11 +1,8 @@
Rails.application.routes.draw do
-
-if ENV['SETTINGS__BULKRAX__ENABLED'] == 'true'
- mount Bulkrax::Engine, at: '/'
-end
- namespace :sony_ci do
- resources :webhook_logs, only: [ :index, :show ]
+ if ENV['SETTINGS__BULKRAX__ENABLED'] == 'true'
+ mount Bulkrax::Engine, at: '/'
end
+
mount Hyrax::BatchIngest::Engine, at: '/'
require 'sidekiq/web'
authenticate :user, lambda { |u| u.admin? } do
@@ -68,8 +65,22 @@
resources 'audits', only: [:new, :create]
post "/audits/new" => "audits#create"
+ # Routes under /sony_ci/*
namespace :sony_ci do
- post '/webhooks/save_sony_ci_id', controller: 'webhooks', action: :save_sony_ci_id
+ # Routes for the Webhook logs.
+ resources :webhook_logs, only: [ :index, :show ]
+
+ # Define routes that receive requests from Sony Ci webhooks.
+ post '/webhooks/save_sony_ci_id', controller: 'webhooks',
+ action: :save_sony_ci_id
+
+ # Define routes for making customized requests to the Sony Ci API
+ get '/api/find_media', controller: 'api', action: :find_media, defaults: { format: :json }
+ get '/api/get_filename', controller: 'api', action: :get_filename, defaults: { format: :json }
+ end
+
+ namespace :api do
+ resources :assets, only: [:show], defaults: { format: :json }
end
diff --git a/db/migrate/20210917032608_add_response_status_and_guids_to_sony_ci_webhook_logs.rb b/db/migrate/20210917032608_add_response_status_and_guids_to_sony_ci_webhook_logs.rb
new file mode 100644
index 000000000..d122f215a
--- /dev/null
+++ b/db/migrate/20210917032608_add_response_status_and_guids_to_sony_ci_webhook_logs.rb
@@ -0,0 +1,10 @@
+class AddResponseStatusAndGuidsToSonyCiWebhookLogs < ActiveRecord::Migration[5.1]
+ def change
+ change_table(:sony_ci_webhook_logs) do |t|
+ t.string :guids
+ t.integer :response_status
+
+ t.index :guids
+ end
+ end
+end
diff --git a/db/schema.test.rb b/db/schema.test.rb
index e69172c30..423af4832 100644
--- a/db/schema.test.rb
+++ b/db/schema.test.rb
@@ -645,6 +645,9 @@
t.string "error_message"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.string "guids"
+ t.integer "response_status"
+ t.index ["guids"], name: "index_sony_ci_webhook_logs_on_guids"
end
create_table "tinymce_assets", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1" do |t|
diff --git a/spec/controllers/api/assets_controller_spec.rb b/spec/controllers/api/assets_controller_spec.rb
new file mode 100644
index 000000000..855c92cf8
--- /dev/null
+++ b/spec/controllers/api/assets_controller_spec.rb
@@ -0,0 +1,70 @@
+require 'rails_helper'
+
+RSpec.describe API::AssetsController, controller: true do
+ describe 'GET /api/assets/{id}' do
+ let(:password) { "abc123" }
+ let(:user) { create(:user, password: password) }
+ let(:encoded_username_and_password) { Base64.encode64("#{request_username}:#{request_password}").strip }
+ let(:format) { :json }
+
+ before do
+ request.headers['Authorization'] = "Basic #{encoded_username_and_password}"
+ get :show, params: { id: asset_id }, format: format
+ end
+
+ context 'when username is wrong,' do
+ let(:request_password) { password }
+ let(:request_username) { 'wrong username' }
+ let(:asset_id) { 'anything' }
+ it 'returns a 401' do
+ expect(response.status).to eq 401
+ end
+ end
+
+ context 'when password is wrong,' do
+ let(:request_username) { user.user_key }
+ let(:request_password) { 'wrong password' }
+ let(:asset_id) { 'anything' }
+ it 'returns a 401' do
+ expect(response.status).to eq 401
+ end
+ end
+
+ context 'when username and password are correct,' do
+ let(:request_username) { user.user_key}
+ let(:request_password) { password }
+
+ context 'when an Asset exists' do
+ let(:asset) { create(:asset) }
+ let(:asset_id) { asset.id }
+ let(:pbcore_xml) { SolrDocument.find(asset_id).export_as_pbcore }
+
+ context 'when the format is .json' do
+ let(:format) { :json }
+ let(:pbcore_json) { Hash.from_xml(pbcore_xml).to_json }
+
+ it 'responds with a 200 status' do
+ expect(response.status).to eq 200
+ end
+
+ it 'response with the JSON for an Asset' do
+ expect(response.body).to eq pbcore_json
+ end
+ end
+
+ context 'when the format is .xml' do
+ let(:format) { :xml }
+
+ it 'responds with a 200 status' do
+ expect(response.status).to eq 200
+ end
+
+ it 'responds with the PBCore XML for an Asset' do
+ pbcore_xml = SolrDocument.find(asset.id).export_as_pbcore
+ expect(response.body).to eq pbcore_xml
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/media_controller_spec.rb b/spec/controllers/media_controller_spec.rb
index 75f329c8f..880d54ca4 100644
--- a/spec/controllers/media_controller_spec.rb
+++ b/spec/controllers/media_controller_spec.rb
@@ -6,19 +6,23 @@
let(:id) { '1234' }
let(:sony_ci_id) { '4567' }
let(:fake_sony_ci_url) { "https://fakesonyci.com/download/#{sony_ci_id}"}
- let(:fake_sony_ci_api) { instance_double("SonyCiBasic") }
+ let(:fake_sony_ci_api) { instance_double(SonyCiApi::Client) }
+ let(:fake_sony_ci_response) {
+ { 'id' => sony_ci_id, 'location' => fake_sony_ci_url }
+ }
let(:fake_solr_document) { { 'sonyci_id_ssim' => [sony_ci_id] } }
before do
- allow(fake_sony_ci_api).to receive(:download).with(sony_ci_id).and_return(fake_sony_ci_url)
+ allow(fake_sony_ci_api).to receive(:asset_download).with(sony_ci_id).and_return(fake_sony_ci_response)
allow(controller).to receive(:ci).and_return(fake_sony_ci_api)
- allow(controller).to receive(:solr_document).and_return(fake_solr_document)
+ allow(SolrDocument).to receive(:find).with(id).and_return(fake_solr_document)
allow(controller).to receive(:can?).with(:show, fake_solr_document).and_return(true)
end
context 'when no Solr document is found for the given :id' do
- # Pretend like the controller failed to find the Solr document.
- before { allow(controller).to receive(:solr_document).and_return(nil) }
+ before do
+ allow(SolrDocument).to receive(:find).with(id).and_raise(Blacklight::Exceptions::RecordNotFound)
+ end
it 'a 404 HTTP status is returned' do
get :show, params: { id: id }
expect(response).to have_http_status 404
@@ -29,9 +33,22 @@
context 'and when user has permission to view the file' do
it 'the SonyCiApi is used to fetch the media url' do
get :show, params: { id: id }
- expect(fake_sony_ci_api).to have_received(:download).with(sony_ci_id)
+ expect(fake_sony_ci_api).to have_received(:asset_download).with(sony_ci_id)
expect(response).to redirect_to fake_sony_ci_url
end
+
+ context 'but the Sony Ci Api responds with any SonyCiApi::HttpError' do
+ let(:http_status) { rand(400..599) }
+ before do
+ allow(fake_sony_ci_api).to receive(:asset_download).and_raise(SonyCiApi::HttpError)
+ allow_any_instance_of(SonyCiApi::HttpError).to receive(:http_status).and_return(http_status)
+ end
+ it 'returns no response and status from SonyCiApi::HttpError#http_status' do
+ get :show, params: { id: id }
+ expect(response).to have_http_status http_status
+ expect(response.body).to be_empty
+ end
+ end
end
context 'and when user does NOT have permission to view the file' do
@@ -40,6 +57,7 @@
it 'a 403 HTTP status is returned' do
get :show, params: { id: id }
expect(response).to have_http_status 403
+ expect(response.body).to be_empty
end
end
end
diff --git a/spec/controllers/sony_ci/api_controller_spec.rb b/spec/controllers/sony_ci/api_controller_spec.rb
new file mode 100644
index 000000000..e953ad2c0
--- /dev/null
+++ b/spec/controllers/sony_ci/api_controller_spec.rb
@@ -0,0 +1,137 @@
+require 'rails_helper'
+
+# TODO: move this.
+RSpec.configure do |config|
+ config.mock_with :rspec do |mocks|
+ mocks.verify_partial_doubles = true
+ end
+end
+
+
+RSpec.describe SonyCi::APIController do
+ # Defind params to use in GET request.
+ let(:params) { { query: "foo" } }
+ let(:mock_sony_ci_api) { instance_double(SonyCiApi::Client) }
+ let(:response_body) { JSON.parse(response.body) }
+
+ before do
+ # Use a mock Sony Ci API in the controller.
+ allow(controller).to receive(:sony_ci_api).and_return(mock_sony_ci_api)
+ end
+
+ # All methods in SonyCi::APIController should catch errors raised by
+ # SonyCiApi::Client and render JSON with the error info, and respond with
+ # the proper HTTP status.
+ shared_examples 'error responses' do |ams_endpoint:, params:|
+ # This context is needed to isolate the `before` callback to specs within
+ # the shared examples.
+ context 'when errors are raised' do
+ let(:error_class) { StandardError }
+ let(:error_msg) { "bad things man, bad things" }
+
+ before do
+ # Force SonyCiApi::Client to raise error_class when any method is called.
+ SonyCiApi::Client.instance_methods(false).each do |instance_method|
+ allow(mock_sony_ci_api).to receive(instance_method).with(any_args).and_raise(
+ error_class,
+ error_msg
+ )
+ end
+ end
+
+ it 'responds with a 500 status and error info' do
+ # make a request to ams_endpoint (param for the shared examples)
+ get ams_endpoint, params: params
+ expect(response_body['error']).to eq "StandardError"
+ expect(response_body['error_message']).to eq error_msg
+ expect(response.status).to eq 500
+ end
+
+
+ context 'when a SonyCiApi::Error is raised' do
+ let(:error_class) { SonyCiApi::Error }
+
+ it 'responds with a 500 http status and JSON object containing the error info' do
+ # make a request to ams_endpoint with params
+ get ams_endpoint, params: params
+ expect(response_body['error']).to eq "SonyCiApi::Error"
+ expect(response_body['error_message']).to eq error_msg
+ expect(response.status).to eq 500
+ end
+ end
+
+ context 'when a SonyCiApi::HttpError is raised' do
+ let(:error_class) { SonyCiApi::HttpError }
+ let(:status) { rand(400..599) }
+
+ before do
+ allow_any_instance_of(SonyCiApi::HttpError).to receive(:http_status).and_return(status)
+ end
+
+ it 'responds with the HTTP status from the error instance' do
+ # make a request to ams_endpoint with params
+ get ams_endpoint, params: params
+ expect(response_body['error']).to eq "SonyCiApi::HttpError"
+ expect(response_body['error_message']).to eq error_msg
+ expect(response.status).to eq status
+ end
+ end
+ end
+ end
+
+ describe 'GET find_media' do
+ before do
+ # the find_media action calls SonyCiApi::Client#workspace_search, so mock
+ # that here as well.
+ allow(mock_sony_ci_api).to receive(:workspace_search).with(
+ hash_including(params)
+ )
+ # Call the action under test.
+ get :find_media, params: params
+ end
+
+ context 'with :query param that returns results from Sony Ci API' do
+ it 'returns a 200 ' do
+ expect(response.status).to eq 200
+ end
+ end
+
+ # Run the 'error responses' shared specs for this endpoint.
+ include_examples 'error responses', ams_endpoint: :find_media, params: { query: 'foo' }
+ end
+
+ describe 'GET get_filename' do
+ let(:params) { { sony_ci_id: '123' } }
+ let(:mock_response) {
+ { 'sony_ci_id' => params[:sony_ci_id], 'name' => 'foo.mp4' }
+ }
+
+ before do
+ allow(mock_sony_ci_api).to receive(:asset).with(params[:sony_ci_id]).and_return(
+ mock_response
+ )
+ end
+
+ # Only use response_body after the request has been made, otherwise it won't
+ # have the real response in it.
+ let(:response_body) { JSON.parse(response.body) }
+
+ it 'gets the filename for a given Sony Ci ID' do
+ get :get_filename, params: params
+ expect(response_body).to eq mock_response
+ expect(response.status).to eq 200
+ end
+
+ context 'when missing the :sony_ci_id param' do
+ let(:params) { {} }
+
+ it 'returns a JSON for 400 Bad Request and says which param is missing' do
+ get :get_filename, params: params
+ expect(response_body['error_message']).to include 'sony_ci_id'
+ expect(response.status).to eq 400
+ end
+ end
+
+ include_examples 'error responses', ams_endpoint: :get_filename, params: { sony_ci_id: '123' }
+ end
+end
diff --git a/spec/controllers/sony_ci/webhook_logs_controller_spec.rb b/spec/controllers/sony_ci/webhook_logs_controller_spec.rb
index bb7a787d4..e8b6c00f3 100644
--- a/spec/controllers/sony_ci/webhook_logs_controller_spec.rb
+++ b/spec/controllers/sony_ci/webhook_logs_controller_spec.rb
@@ -1,58 +1,22 @@
require 'rails_helper'
-# This spec was generated by rspec-rails when you ran the scaffold generator.
-# It demonstrates how one might use RSpec to specify the controller code that
-# was generated by Rails when you ran the scaffold generator.
-#
-# It assumes that the implementation code is generated by the rails scaffold
-# generator. If you are using any extension libraries to generate different
-# controller code, this generated spec may or may not pass.
-#
-# It only uses APIs available in rails and/or rspec-rails. There are a number
-# of tools you can use to make these specs even more expressive, but we're
-# sticking to rails and rspec-rails APIs to keep things simple and stable.
-#
-# Compared to earlier versions of this generator, there is very limited use of
-# stubs and message expectations in this spec. Stubs are only used when there
-# is no simpler way to get a handle on the object needed for the example.
-# Message expectations are only used when there is no simpler way to specify
-# that an instance is receiving a specific message.
-#
-# Also compared to earlier versions of this generator, there are no longer any
-# expectations of assigns and templates rendered. These features have been
-# removed from Rails core in Rails 5, but can be added back in via the
-# `rails-controller-testing` gem.
-
RSpec.describe SonyCi::WebhookLogsController, type: :controller do
- # This should return the minimal set of attributes required to create a valid
- # SonyCi::WebhookLog. As you add validations to SonyCi::WebhookLog, be sure to
- # adjust the attributes here as well.
- let(:valid_attributes) {
- skip("Add a hash of attributes valid for your model")
- }
-
- let(:invalid_attributes) {
- skip("Add a hash of attributes invalid for your model")
- }
-
- # This should return the minimal set of values that should be in the session
- # in order to pass any filters (e.g. authentication) defined in
- # SonyCi::WebhookLogsController. Be sure to keep this updated too.
- let(:valid_session) { {} }
+ render_views
describe "GET #index" do
it "returns a success response" do
- SonyCi::WebhookLog.create! valid_attributes
- get :index, params: {}, session: valid_session
expect(response).to be_successful
end
end
describe "GET #show" do
+ let(:webhook_log) { create(:sony_ci_webhook_log) }
it "returns a success response" do
- webhook_log = SonyCi::WebhookLog.create! valid_attributes
- get :show, params: {id: webhook_log.to_param}, session: valid_session
+ get :show, params: { id: webhook_log.to_param }
+
+
+
expect(response).to be_successful
end
end
diff --git a/spec/controllers/sony_ci/webhooks_controller_spec.rb b/spec/controllers/sony_ci/webhooks_controller_spec.rb
index daa8fc0dd..defa296a8 100644
--- a/spec/controllers/sony_ci/webhooks_controller_spec.rb
+++ b/spec/controllers/sony_ci/webhooks_controller_spec.rb
@@ -1,24 +1,18 @@
require 'rails_helper'
RSpec.describe SonyCi::WebhooksController do
-
- # non-memoized shortcut to random hex string
- def randhex(len=32)
- len.times.map { rand(15).to_s(16) }
- end
-
describe 'POST save_sony_ci_id' do
- let(:sony_ci_id) { randhex }
+ let(:sony_ci_id) { Faker::Number.hexadecimal(16) }
let(:asset) { create(:asset) }
let(:sony_ci_filename) { "#{asset.id}.mp4" }
let(:request_body) {
{
- "id" => randhex,
+ "id" => Faker::Number.hexadecimal(16),
"type" => "AssetProcessingFinished",
"createdOn" => Time.now.utc.iso8601,
"createdBy" => {
- "id" => randhex,
+ "id" => Faker::Number.hexadecimal(16),
"name" => "John Smith",
"email" => "johnsmith@example.com"
},
@@ -42,23 +36,27 @@ def randhex(len=32)
after do
expect(latest_webhook_log.request_body).to eq request_body
expect(latest_webhook_log.response_body).to eq response_body
+ expect(latest_webhook_log.response_status).to eq 200
end
it 'returns a 200 ' \
- 'and returns a success message ' \
- 'and saves the Sony Ci ID to the Asset ' \
- 'and creates a WebhookRequest record for logging' do
+ 'and returns a success message, ' \
+ 'and saves the Sony Ci ID to the Asset, ' \
+ 'and creates a WebhookLog record for logging containing the GUID' do
expect(response.status).to eq 200
expect(response_body['message']).to match /success/
expect(asset.admin_data.reload.sonyci_id).to eq [ sony_ci_id ]
+ expect(latest_webhook_log.guids).to eq [ asset.id ]
end
context 'when the uploaded filename does not resolve to an Asset' do
let(:sony_ci_filename) { "does-not-match-asset.mp4" }
- it 'returns a 200 to avoid Sony Ci retries' \
- 'and returns an error message in the body' do
+ it 'responds with a 200 to avoid Sony Ci retries, ' \
+ 'and responds with the error message, ' \
+ 'and does NOT save the GUID to the WebhookLog record' do
expect(response.status).to eq 200
expect(response_body['error']).not_to be_empty
+ expect(latest_webhook_log.guids).to be_empty
end
end
end
diff --git a/spec/factories/sony_ci/webhook_log.rb b/spec/factories/sony_ci/webhook_log.rb
new file mode 100644
index 000000000..fb209f1ac
--- /dev/null
+++ b/spec/factories/sony_ci/webhook_log.rb
@@ -0,0 +1,53 @@
+FactoryBot.define do
+ factory :sony_ci_webhook_log, class: 'SonyCi::WebhookLog' do
+ url { Faker::Internet.url }
+ action { SonyCi::WebhooksController.action_methods.to_a.sample }
+ request_headers { { 'Content-Type' => 'application/json' } }
+ request_body {
+ {
+ "id": "6vzdrfgzff0teglw",
+ "type": "AssetProcessingFinished",
+ "createdOn": "2017-01-02T00:00:00.000Z",
+ "createdBy": {
+ "id": "c460dfc1447f4240b14b2f32ce8d4a5f",
+ "name": "John Smith",
+ "email": "johnsmith@example.com"
+ },
+ "assets": [
+ {
+ "id": "kayc4skb5dkk49k7",
+ "name": "Movie.mov"
+ }
+ ]
+ }
+ }
+
+ # Response headers should always include Content-type of application/json.
+ response_headers { { "Content-Type" => "application/json" } }
+
+ # NOTE: the response status does not necesarily mean that no error occurred.
+ # Rather, returning a 2xx status tells Sony Ci not to try the request again
+ # which is what we want in nearly every use case.
+ response_status { 200 }
+
+ # Randomly assing an error.
+ error { [true, false].sample ? "FakeError" : nil }
+
+ # Almost always, there is only 1 GUID, but need to allow for multiple.
+ guids { [ "cpb-aacip-#{Faker::Number.hexadecimal(11)}" ] }
+
+ after(:build) do |webhook_log|
+ if webhook_log.error?
+ # Add an error message if we have an error.
+ webhook_log.error_message = Faker::Lorem.sentence if webhook_log.error?
+ # TODO: This should match what is actually returned by
+ # SonyCi::WebhooksController for the given error.
+ webhook_log.response_body = { "error" => webhook_log.error_message }
+ else
+ # TODO: This should match what is actually returned by
+ # SonyCi::WebhooksController for the given action.
+ webhook_log.response_body = { "success" => true }
+ end
+ end
+ end
+end
diff --git a/spec/factories/sony_ci_webhook_logs.rb b/spec/factories/sony_ci_webhook_logs.rb
deleted file mode 100644
index 7d33ac262..000000000
--- a/spec/factories/sony_ci_webhook_logs.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-FactoryBot.define do
- factory :sony_ci_webhook_log, class: 'SonyCi::WebhookLog' do
-
- end
-end
diff --git a/spec/features/update_admin_data_spec.rb b/spec/features/update_admin_data_spec.rb
index 8587c7743..455e13d0d 100644
--- a/spec/features/update_admin_data_spec.rb
+++ b/spec/features/update_admin_data_spec.rb
@@ -3,7 +3,7 @@
# This is really a test of work done in the AssetActor
# Actor classes are very hard to test due to attempting to mock the entire environment,
# so this indirectly and imperfectly tests our saving expections
-RSpec.feature 'Update AdminData', js: true, asset_form_helpers: true, clean: true do
+RSpec.feature 'Update AdminData', asset_form_helpers: true, clean: true do
context 'Create adminset, create asset' do
let(:admin_user) { create :admin_user }
let(:admin_set_id) { AdminSet.find_or_create_default_admin_set_id }
@@ -11,12 +11,24 @@
let!(:workflow) { Sipity::Workflow.create!(active: true, name: 'test-workflow', permission_template: permission_template) }
let!(:admindata) { create(:admin_data, :empty)}
let!(:asset) { FactoryBot.create(:asset, with_admin_data: admindata.gid) }
+ let(:fake_sonyci_id) { rand(999999) }
+ let(:fake_sonyci_records) {
+ { fake_sonyci_id: { 'id' => fake_sonyci_id, 'name' => 'foo' } }
+ }
let(:admin_data_string_attributes) {
{
- "Sony's Ci ID" => "1a2b3c4d5e"
+ "Sony's Ci ID" => fake_sonyci_id
}
}
+ before do
+ # It's not apparent, but this is required to avoid errors.
+ # When the Sony Ci ID input is rendered, AdminData#sonyci_records is
+ # called. which calls SonyCiApi::Client#asset to fetch the records.
+ # NOTE: confusingly, the Sony Ci API refers to the records as 'assets'.
+ allow_any_instance_of(SonyCiApi::Client).to receive(:asset).with(fake_sonyci_id.to_s).and_return(fake_sonyci_records)
+ end
+
scenario 'Update AdminData on Asset' do
Sipity::WorkflowAction.create!(name: 'submit', workflow: workflow)
Hyrax::PermissionTemplateAccess.create!(
diff --git a/spec/models/asset_spec.rb b/spec/models/asset_spec.rb
index fefe1e1c4..133bac2c3 100644
--- a/spec/models/asset_spec.rb
+++ b/spec/models/asset_spec.rb
@@ -6,7 +6,6 @@
let(:asset) { build(:asset) }
context 'properties' do
-
subject { build :asset }
it { is_expected.to have_property(:bulkrax_identifier).with_predicate("http://ams2.wgbh-mla.org/resource#bulkraxIdentifier") }
@@ -45,16 +44,33 @@
it { is_expected.to have_property(:producing_organization).with_predicate(::RDF::Vocab::DC11.creator) }
end
- context "admin_data_gid" do
+ context "with AdminData" do
let(:admin_data) { FactoryBot.create(:admin_data) }
- let(:asset) { FactoryBot.build(:asset, with_admin_data:admin_data.gid) }
- it "has admin_data_gid" do
- expect(asset).to have_property(:admin_data_gid).with_predicate(/pbcore.org#hasAAPBAdminData/)
- expect(asset.admin_data_gid).to eq(admin_data.gid)
+ let(:annotation) { FactoryBot.create(:annotation, admin_data_id: admin_data.id)}
+ let(:asset) { FactoryBot.build(:asset, with_admin_data: admin_data.gid) }
+
+ describe ".admin_data_gid" do
+ it 'returns the expected AdminData' do
+ expect(asset).to have_property(:admin_data_gid).with_predicate(/pbcore.org#hasAAPBAdminData/)
+ expect(asset.admin_data_gid).to eq(admin_data.gid)
+ end
+
+ it "returns ActiveRecord::RecordNotFound if cannot find admin_data for the gid" do
+ gid = 'gid://ams/admindata/999'
+ expect { asset.admin_data_gid = gid }.to raise_error(ActiveRecord::RecordNotFound, "Couldn't find AdminData matching GID #{gid}")
+ end
end
- it "has throws ActiveRecord::RecordNotFound if cannot find admin_data for the gid" do
- gid = 'gid://ams/admindata/999'
- expect { asset.admin_data_gid = gid }.to raise_error(ActiveRecord::RecordNotFound, "Couldn't find AdminData matching GID #{gid}")
+
+ describe ".find_admin_data_attribute" do
+ it 'returns the expected value' do
+ expect(asset.find_admin_data_attribute("sonyci_id")).to eq(admin_data.sonyci_id)
+ end
+ end
+
+ describe '.find_annotation_attribute' do
+ it 'returns the expected value' do
+ expect(asset.find_annotation_attribute(annotation.annotation_type)).to eq([annotation.value])
+ end
end
end
diff --git a/spec/models/sony_ci/webhook_log_spec.rb b/spec/models/sony_ci/webhook_log_spec.rb
index 502a0fb82..bfbc4544e 100644
--- a/spec/models/sony_ci/webhook_log_spec.rb
+++ b/spec/models/sony_ci/webhook_log_spec.rb
@@ -1,5 +1,42 @@
require 'rails_helper'
RSpec.describe SonyCi::WebhookLog, type: :model do
- pending "add some examples to (or delete) #{__FILE__}"
+ describe 'validation' do
+ subject { build(:sony_ci_webhook_log) }
+ context 'when all fields are valid' do
+ it { is_expected.to be_valid }
+ end
+
+ context 'when response_status not a number between 200 and 599' do
+ before { subject.response_status = 'something bogus' }
+ it { is_expected.to_not be_valid }
+ end
+
+ context 'when URL is empty' do
+ it 'is invalid' do
+ expect(build(:sony_ci_webhook_log, url: nil)).to_not be_valid
+ expect(build(:sony_ci_webhook_log, url: '')).to_not be_valid
+ end
+ end
+
+ context 'when URL is empty string' do
+ before { subject.url = '' }
+ it { is_expected.to_not be_valid }
+ end
+
+ context 'when action is empty' do
+ it 'is invalid' do
+ expect(build(:sony_ci_webhook_log, action: nil)).to_not be_valid
+ expect(build(:sony_ci_webhook_log, action: '')).to_not be_valid
+ end
+ end
+ end
+
+ describe '#guids' do
+ let(:guids) { [ 'cpb-aacip-1234', 'cpb-aacip-5678'] }
+ let(:webhook_log) { create(:sony_ci_webhook_log, guids: guids) }
+ it 'stores a list of GUIDs as a serialized array' do
+ expect(SonyCi::WebhookLog.find(webhook_log.id).guids).to eq guids
+ end
+ end
end
diff --git a/spec/presenters/sony_ci/webhook_log_presenter_spec.rb b/spec/presenters/sony_ci/webhook_log_presenter_spec.rb
new file mode 100644
index 000000000..0505d0ef1
--- /dev/null
+++ b/spec/presenters/sony_ci/webhook_log_presenter_spec.rb
@@ -0,0 +1,44 @@
+# Generated via
+# `rails generate hyrax:work Asset`
+require 'rails_helper'
+
+RSpec.describe SonyCi::WebhookLogPresenter do
+
+ let(:webhook_log) {
+ create(:sony_ci_webhook_log, action: 'save_sony_ci_id')
+ }
+ let(:presenter) do
+ described_class.new(webhook_log)
+ end
+
+ describe '#created_at' do
+ it 'returns the created_at timestamp field formatted to mm/dd/yyyy hh:mm:ss am/pm' do
+ expected_date_str = webhook_log.created_at.strftime(described_class::DATETIME_FORMAT)
+ expect(presenter.created_at).to eq expected_date_str
+ end
+ end
+
+ describe '#action' do
+ it 'returns a user-friendly name of the action performed' do
+ expect(presenter.action).to eq 'Link Asset to Sony Ci Media'
+ end
+ end
+
+ describe '#status' do
+ context 'when there is an error present' do
+ let(:webhook_log) { create(:sony_ci_webhook_log, error: "Some error") }
+ it 'returns "Fail"' do
+ expect(presenter.status).to eq "Fail"
+ end
+ end
+ end
+
+ describe '#status' do
+ context 'when there is not an error present' do
+ let(:webhook_log) { create(:sony_ci_webhook_log, error: nil) }
+ it 'returns "Success"' do
+ expect(presenter.status).to eq "Success"
+ end
+ end
+ end
+end
diff --git a/spec/services/ams/media_download/media_download_service_spec.rb b/spec/services/ams/media_download/media_download_service_spec.rb
index 7249d3ded..c914da041 100644
--- a/spec/services/ams/media_download/media_download_service_spec.rb
+++ b/spec/services/ams/media_download/media_download_service_spec.rb
@@ -5,10 +5,19 @@
let(:admin_data) { create(:admin_data, :one_sony_ci_id) }
let(:asset) { create(:asset, with_admin_data: admin_data.gid) }
- let(:fake_sony_ci_url) { "https://fake_sony_ci_url/cifiles/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4?response-content-disposition=attachment%3bfilename%3d%22cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4"}
+ # Pared down response from Sony Ci. For this spec, we just really need the
+ # 'id' and 'location'.
+ let(:fake_sony_ci_api_result) {
+ {
+ 'id' => sonyci_id,
+ 'location' => "https://fake_sony_ci_url/cifiles/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4?response-content-disposition=attachment%3bfilename%3d%22cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4"
+ }
+ }
+ # let(:fake_sony_ci_url) { "https://fake_sony_ci_url/cifiles/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4?response-content-disposition=attachment%3bfilename%3d%22cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4"}
let(:spec_media_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4' ) }
- let(:fake_sony_ci_api) { instance_double("SonyCiBasic") }
+ let(:fake_sony_ci_api) { instance_double(SonyCiApi::Client) }
let(:solr_doc) { SolrDocument.new(asset.to_solr) }
+ let(:sonyci_id) { solr_doc['sonyci_id_ssim'].first }
let(:service) do
described_class.new(solr_document: solr_doc)
@@ -17,7 +26,7 @@
before do
allow(service).to receive(:ci).and_return(fake_sony_ci_api)
allow(service).to receive(:generate_sonyci_file_path).with('cpb-aacip-15-hd7np1wp4c__barcode163700_.h264.mp4').and_return(spec_media_file_path)
- allow(service).to receive(:download_media_file).with(spec_media_file_path, fake_sony_ci_url)
+ allow(service).to receive(:download_media_file).with(spec_media_file_path, fake_sony_ci_api_result['location'])
allow(service).to receive(:delete_media_files)
end
@@ -25,7 +34,7 @@
context "with a single Sony Ci ID" do
context "during a successful download" do
before do
- allow(fake_sony_ci_api).to receive(:download).with(/Sony-\d{1}/).and_return(fake_sony_ci_url)
+ allow(fake_sony_ci_api).to receive(:asset_download).with(/Sony-\d{1}/).and_return(fake_sony_ci_api_result)
end
it "returns the expected Success object" do
@@ -39,7 +48,7 @@
context "during an unsuccessful download" do
before do
- allow(fake_sony_ci_api).to receive(:download).with(/Sony-\d{1}/).and_raise(RuntimeError.new("NO VIDEO!!!"))
+ allow(fake_sony_ci_api).to receive(:asset_download).with(sonyci_id).and_raise(RuntimeError.new("NO VIDEO!!!"))
end
it "returns the expected Failure object" do
diff --git a/spec/views/sony_ci/webhook_logs/index.html.erb_spec.rb b/spec/views/sony_ci/webhook_logs/index.html.erb_spec.rb
index 25a3ab6e6..77146bc36 100644
--- a/spec/views/sony_ci/webhook_logs/index.html.erb_spec.rb
+++ b/spec/views/sony_ci/webhook_logs/index.html.erb_spec.rb
@@ -1,14 +1,26 @@
require 'rails_helper'
RSpec.describe "sony_ci/webhook_logs/index", type: :view do
+ let(:webhook_logs) { create_list(:sony_ci_webhook_log, rand(3..7)) }
+ let(:presenters) {
+ webhook_logs.map { |webhook_log|
+ SonyCi::WebhookLogPresenter.new(webhook_log)
+ }
+ }
+
before(:each) do
- assign(:sony_ci_webhook_logs, [
- SonyCi::WebhookLog.create!(),
- SonyCi::WebhookLog.create!()
- ])
+ assign(:presenters, presenters)
+ render
end
it "renders a list of sony_ci/webhook_logs" do
- render
+ presenters.each do |presenter|
+ expect(rendered).to include presenter.created_at
+ expect(rendered).to include presenter.action
+ expect(rendered).to include presenter.status
+ presenter.guids.each do |guid|
+ expect(rendered).to include guid
+ end
+ end
end
end
diff --git a/spec/views/sony_ci/webhook_logs/show.html.erb_spec.rb b/spec/views/sony_ci/webhook_logs/show.html.erb_spec.rb
index 0ccae64ef..e590d32b8 100644
--- a/spec/views/sony_ci/webhook_logs/show.html.erb_spec.rb
+++ b/spec/views/sony_ci/webhook_logs/show.html.erb_spec.rb
@@ -1,11 +1,17 @@
require 'rails_helper'
RSpec.describe "sony_ci/webhook_logs/show", type: :view do
+ let(:webhook_log) { create(:sony_ci_webhook_log) }
+ let(:presenter) { SonyCi::WebhookLogPresenter.new(webhook_log) }
before(:each) do
- @sony_ci_webhook_log = assign(:sony_ci_webhook_log, SonyCi::WebhookLog.create!())
+ assign(:presenter, presenter)
+ render
end
- it "renders attributes in " do
- render
+ it 'displays the Date, Action, URL, Request Headers, Request Body, ' \
+ 'Response Headers, Response Body, and the Error, if present' do
+ expect(rendered).to include presenter.created_at
+ expect(rendered).to include presenter.action
+ expect(rendered).to include presenter.url
end
end