Create new InstFS references for RCE links
closes RCX-1900 flag=rce_linked_file_urls Test plan - Have InstFS running and enabled with Canvas - Run API calls to api/v1/rce_linked_file_urls - Send an arbitrary "location" parameter - Send a user_uuid parameter (with access, without, see what happens) - Send an array of file_urls with various types of Canvas files that aren't previewable with quizzes (should be media and documents that aren't) - Verify that you get a new InstFS uuid for the files - Extra credit if you check that the location is saved properly on the new file in InstFS's DynamoDB Change-Id: I4d7eb1aeb62a515360fc4668acbe38caa8f94ed8 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/354570 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: James Logan <james.logan@instructure.com> Reviewed-by: Eric Saupe <eric.saupe@instructure.com> QA-Review: James Logan <james.logan@instructure.com> Product-Review: Mysti Lilla <mysti@instructure.com>
This commit is contained in:
parent
523469c087
commit
bcc2dd549b
|
@ -1572,7 +1572,7 @@ class FilesController < ApplicationController
|
|||
nil
|
||||
end
|
||||
att_ids = parsed_file_urls.pluck(:id, :attachment_id, :file_id).flatten.compact
|
||||
att_list = Attachment.where(id: att_ids).merge(Attachment.active.or(Attachment.where.not(replacement_attachment_id: nil))).preload(:context, replacement_attachment: :context)
|
||||
att_list = Attachment.where(id: att_ids).merge(Attachment.not_deleted.or(Attachment.where.not(replacement_attachment_id: nil))).preload(:context, replacement_attachment: :context)
|
||||
att_hash_list = att_list.index_by { |att| att.id.to_s }
|
||||
|
||||
file_context_keys = %i[account_id course_id group_id user_id].freeze
|
||||
|
@ -1581,7 +1581,7 @@ class FilesController < ApplicationController
|
|||
file_id = parsed_file_url[:id] || parsed_file_url[:attachment_id] || parsed_file_url[:file_id]
|
||||
att = att_hash_list[file_id]
|
||||
if att&.replacement_attachment_id
|
||||
parsed_file_url[:old_att_id] = att.id
|
||||
parsed_file_url[:id] ||= att.id
|
||||
att = att.replacement_attachment
|
||||
end
|
||||
next unless att
|
||||
|
@ -1599,18 +1599,32 @@ class FilesController < ApplicationController
|
|||
next unless context.grants_any_right?(@current_user, session, :manage_files_create, :manage_files_edit, :moderate_user_content, :become_user) &&
|
||||
context.grants_any_right?(user, session, :manage_files_create, :manage_files_edit)
|
||||
|
||||
canvas_display_files = []
|
||||
file_list.each do |file|
|
||||
att = file[:attachment]
|
||||
if att.media_entry_id.present? || att.canvadocable?
|
||||
file_metadata[:canvas_urls] ||= {}
|
||||
url = file[:url]
|
||||
old_att_id = file[:old_att_id]
|
||||
file_metadata[:canvas_urls][file[:url]] = old_att_id.present? ? url.sub(%r{(files|iframe)/#{old_att_id}}, "\\1/#{att.id}") : url
|
||||
canvas_display_files << file
|
||||
else
|
||||
file_metadata[:instfs_ids] ||= {}
|
||||
file_metadata[:instfs_ids][file[:url]] = att.instfs_uuid
|
||||
end
|
||||
end
|
||||
|
||||
canvas_display_files.each do |file|
|
||||
file_metadata[:canvas_urls] ||= {}
|
||||
# TODO: Work with InstFS to make a bulk duplicate API for this
|
||||
url = file[:url]
|
||||
att = file[:attachment]
|
||||
unless params[:location].present? && (new_instfs_ref = att.create_rce_reference(params[:location]))
|
||||
file_metadata[:canvas_urls][url] = url
|
||||
next
|
||||
end
|
||||
old_att_id = file[:id] if file[:id] != att.id.to_s
|
||||
new_url = old_att_id.present? ? url.sub(%r{(files|iframe)/#{old_att_id}}, "\\1/#{att.id}") : url
|
||||
parsed_url = Addressable::URI.parse(CGI.unescape_html(new_url))
|
||||
parsed_url.query_values = (parsed_url.query_values || {}).merge({ instfs_id: new_instfs_ref })
|
||||
file_metadata[:canvas_urls][url] = parsed_url.to_s
|
||||
end
|
||||
end
|
||||
|
||||
return render json: file_urls_with_uuids.to_json, status: :created if file_urls_with_uuids.present?
|
||||
|
|
|
@ -1783,6 +1783,20 @@ class Attachment < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def create_rce_reference(location, context: nil)
|
||||
if instfs_hosted? && InstFS.enabled?
|
||||
tenant_auth = { location: }
|
||||
if context.present?
|
||||
tenant_auth[:application] = Lti::Oauth2::AccessToken::ISS
|
||||
tenant_auth[:context_type] = context.table_name
|
||||
tenant_auth[:context_uuid] = context.uuid
|
||||
tenant_auth[:account_uuid] = context.account.uuid
|
||||
tenant_auth[:root_account_uuid] = context.root_account.uuid
|
||||
end
|
||||
InstFS.duplicate_file(instfs_uuid, tenant_auth:)
|
||||
end
|
||||
end
|
||||
|
||||
def self.file_removed_path
|
||||
Rails.public_path.join("file_removed/file_removed.pdf")
|
||||
end
|
||||
|
|
|
@ -265,8 +265,8 @@ module InstFS
|
|||
json_response["success"][0]["id"]
|
||||
end
|
||||
|
||||
def duplicate_file(instfs_uuid)
|
||||
token = duplicate_file_jwt(instfs_uuid)
|
||||
def duplicate_file(instfs_uuid, tenant_auth: nil)
|
||||
token = duplicate_file_jwt(instfs_uuid, tenant_auth:)
|
||||
url = "#{app_host}/files/#{instfs_uuid}/duplicate?token=#{token}"
|
||||
|
||||
response = CanvasHttp.post(url)
|
||||
|
@ -462,12 +462,13 @@ module InstFS
|
|||
SHORT_JWT_EXPIRATION)
|
||||
end
|
||||
|
||||
def duplicate_file_jwt(instfs_uuid)
|
||||
service_jwt({
|
||||
iat: Time.now.utc.to_i,
|
||||
resource: "/files/#{instfs_uuid}/duplicate"
|
||||
},
|
||||
SHORT_JWT_EXPIRATION)
|
||||
def duplicate_file_jwt(instfs_uuid, tenant_auth: nil)
|
||||
jwt_contents = {
|
||||
iat: Time.now.utc.to_i,
|
||||
resource: "/files/#{instfs_uuid}/duplicate"
|
||||
}
|
||||
jwt_contents[:tenant_auth] = tenant_auth if tenant_auth.present?
|
||||
service_jwt(jwt_contents, SHORT_JWT_EXPIRATION)
|
||||
end
|
||||
|
||||
def delete_file_jwt(instfs_uuid)
|
||||
|
|
|
@ -1687,6 +1687,7 @@ describe "Files API", type: :request do
|
|||
account_admin_user(account: @course.root_account)
|
||||
user_session(@user)
|
||||
allow(Canvadocs).to receive(:enabled?).and_return(true)
|
||||
allow(InstFS).to receive_messages(enabled?: true, app_host: "http://instfs.test")
|
||||
end
|
||||
|
||||
it "returns 404 if feature not enabled" do
|
||||
|
@ -1695,24 +1696,29 @@ describe "Files API", type: :request do
|
|||
end
|
||||
|
||||
it "allows access to course files the user has access to manage" do
|
||||
doc = attachment_model(context: @course, display_name: "test.docx", uploaded_data: fixture_file_upload("test.docx"), instfs_uuid: "doc")
|
||||
image = attachment_model(context: @course, display_name: "cn_image.jpg", uploaded_data: fixture_file_upload("cn_image.jpg"), instfs_uuid: "image")
|
||||
media = attachment_model(context: @course, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media")
|
||||
course = @course
|
||||
doc = attachment_model(context: course, display_name: "test.docx", uploaded_data: fixture_file_upload("test.docx"), instfs_uuid: "doc")
|
||||
image = attachment_model(context: course, display_name: "cn_image.jpg", uploaded_data: fixture_file_upload("cn_image.jpg"), instfs_uuid: "image")
|
||||
media = attachment_model(context: course, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media")
|
||||
diff_course = attachment_model(context: course_factory, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media2")
|
||||
|
||||
file_urls = [
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1",
|
||||
"/courses/#{@course.id}/files/#{image.id}/preview",
|
||||
"/courses/#{course.id}/files/#{doc.id}?wrap=1",
|
||||
"/courses/#{course.id}/files/#{image.id}/preview",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
"/media_attachments_iframe/#{diff_course.id}?type=video&embedded=true"
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
stub_request(:post, %r{^http://instfs.test/files/doc/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_doc_id" }.to_json)
|
||||
stub_request(:post, %r{^http://instfs.test/files/media/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_media_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"instfs_ids" => { "/courses/#{@course.id}/files/#{image.id}/preview" => "image" },
|
||||
"instfs_ids" => { "/courses/#{course.id}/files/#{image.id}/preview" => "image" },
|
||||
"canvas_urls" => {
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1" => "/courses/#{@course.id}/files/#{doc.id}?wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?type=video&embedded=true"
|
||||
"/courses/#{course.id}/files/#{doc.id}?wrap=1" => "/courses/#{course.id}/files/#{doc.id}?instfs_id=new_doc_id&wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?embedded=true&instfs_id=new_media_id&type=video"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
@ -1721,21 +1727,25 @@ describe "Files API", type: :request do
|
|||
doc = attachment_model(context: @teacher, display_name: "test.docx", uploaded_data: fixture_file_upload("test.docx"), instfs_uuid: "doc")
|
||||
image = attachment_model(context: @teacher, display_name: "cn_image.jpg", uploaded_data: fixture_file_upload("cn_image.jpg"), instfs_uuid: "image")
|
||||
media = attachment_model(context: @teacher, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media")
|
||||
not_yours = attachment_model(context: @user, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media")
|
||||
|
||||
file_urls = [
|
||||
"/users/#{@teacher.id}/files/#{doc.id}?wrap=1",
|
||||
"/users/#{@teacher.id}/files/#{image.id}/preview",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
"/media_attachments_iframe/#{not_yours.id}?type=video&embedded=true"
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
stub_request(:post, %r{^http://instfs.test/files/doc/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_doc_id" }.to_json)
|
||||
stub_request(:post, %r{^http://instfs.test/files/media/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_media_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"instfs_ids" => { "/users/#{@teacher.id}/files/#{image.id}/preview" => "image" },
|
||||
"canvas_urls" => {
|
||||
"/users/#{@teacher.id}/files/#{doc.id}?wrap=1" => "/users/#{@teacher.id}/files/#{doc.id}?wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?type=video&embedded=true"
|
||||
"/users/#{@teacher.id}/files/#{doc.id}?wrap=1" => "/users/#{@teacher.id}/files/#{doc.id}?instfs_id=new_doc_id&wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?embedded=true&instfs_id=new_media_id&type=video"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
@ -1745,14 +1755,15 @@ describe "Files API", type: :request do
|
|||
|
||||
file_urls = ["/files/#{doc.id}/download?download_frd=1", "/files/#{doc.id}", "http://example.canvas.edu/files/#{doc.id}/download"]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
stub_request(:post, %r{^http://instfs.test/files/doc/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_doc_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"canvas_urls" => {
|
||||
"/files/#{doc.id}/download?download_frd=1" => "/files/#{doc.id}/download?download_frd=1",
|
||||
"/files/#{doc.id}" => "/files/#{doc.id}",
|
||||
"http://example.canvas.edu/files/#{doc.id}/download" => "http://example.canvas.edu/files/#{doc.id}/download"
|
||||
"/files/#{doc.id}/download?download_frd=1" => "/files/#{doc.id}/download?download_frd=1&instfs_id=new_doc_id",
|
||||
"/files/#{doc.id}" => "/files/#{doc.id}?instfs_id=new_doc_id",
|
||||
"http://example.canvas.edu/files/#{doc.id}/download" => "http://example.canvas.edu/files/#{doc.id}/download?instfs_id=new_doc_id"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
@ -1769,12 +1780,37 @@ describe "Files API", type: :request do
|
|||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 422)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 422)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({ "errors" => [{ "message" => "No valid file URLs given" }] })
|
||||
end
|
||||
|
||||
it "shows hidden files" do
|
||||
doc = attachment_model(context: @course, display_name: "test.docx", uploaded_data: fixture_file_upload("test.docx"), instfs_uuid: "doc", file_state: "hidden")
|
||||
image = attachment_model(context: @course, display_name: "cn_image.jpg", uploaded_data: fixture_file_upload("cn_image.jpg"), instfs_uuid: "image", file_state: "hidden")
|
||||
media = attachment_model(context: @course, display_name: "292.mp3", uploaded_data: fixture_file_upload("292.mp3"), instfs_uuid: "media", file_state: "hidden")
|
||||
|
||||
file_urls = [
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1",
|
||||
"/courses/#{@course.id}/files/#{image.id}/preview",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
stub_request(:post, %r{^http://instfs.test/files/doc/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_doc_id" }.to_json)
|
||||
stub_request(:post, %r{^http://instfs.test/files/media/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_media_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"instfs_ids" => { "/courses/#{@course.id}/files/#{image.id}/preview" => "image" },
|
||||
"canvas_urls" => {
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1" => "/courses/#{@course.id}/files/#{doc.id}?instfs_id=new_doc_id&wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?embedded=true&instfs_id=new_media_id&type=video"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
it "follows replaced files" do
|
||||
doc2 = attachment_model(context: @course, display_name: "test.docx", uploaded_data: fixture_file_upload("test.docx"), instfs_uuid: "doc2")
|
||||
image2 = attachment_model(context: @course, display_name: "cn_image.jpg", uploaded_data: fixture_file_upload("cn_image.jpg"), instfs_uuid: "image2")
|
||||
|
@ -1791,14 +1827,16 @@ describe "Files API", type: :request do
|
|||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
stub_request(:post, %r{^http://instfs.test/files/doc2/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_doc2_id" }.to_json)
|
||||
stub_request(:post, %r{^http://instfs.test/files/media2/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_media2_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"instfs_ids" => { "/courses/#{@course.id}/files/#{image.id}/preview" => "image2" },
|
||||
"canvas_urls" => {
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1" => "/courses/#{@course.id}/files/#{doc2.id}?wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media2.id}?type=video&embedded=true"
|
||||
"/courses/#{@course.id}/files/#{doc.id}?wrap=1" => "/courses/#{@course.id}/files/#{doc2.id}?instfs_id=new_doc2_id&wrap=1",
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media2.id}?embedded=true&instfs_id=new_media2_id&type=video"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
@ -1812,12 +1850,13 @@ describe "Files API", type: :request do
|
|||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true",
|
||||
]
|
||||
body = { user_uuid: @teacher.uuid, file_urls:, location: "quiz/123" }
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
stub_request(:post, %r{^http://instfs.test/files/media/duplicate\?token=}).to_return(status: 201, body: { "id" => "new_media_id" }.to_json)
|
||||
|
||||
api_call(:post, "/api/v1/rce_linked_file_urls", { controller: "files", action: "rce_linked_file_urls", format: "json" }, body, {}, expected_status: 201)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json).to eq({
|
||||
"canvas_urls" => {
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?type=video&embedded=true"
|
||||
"/media_attachments_iframe/#{media.id}?type=video&embedded=true" => "/media_attachments_iframe/#{media.id}?embedded=true&instfs_id=new_media_id&type=video"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue