Extract canvas_http into seperate gem.
Change-Id: Ib90c12b99d30853a0a1a0235c9aa1b5dd645f614 fixes: CNVS-11949 Reviewed-on: https://gerrit.instructure.com/32176 Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Simon Williams <simon@instructure.com> Reviewed-by: Stanley Stuart <stanley@instructure.com> QA-Review: August Thornton <august@instructure.com>
This commit is contained in:
parent
b9d3460aaa
commit
ccd8b38d6a
|
@ -118,6 +118,7 @@ gem 'adheres_to_policy', :path => 'gems/adheres_to_policy'
|
|||
gem 'canvas_breach_mitigation', :path => 'gems/canvas_breach_mitigation'
|
||||
gem 'canvas_color', :path => 'gems/canvas_color'
|
||||
gem 'canvas_crummy', :path => 'gems/canvas_crummy'
|
||||
gem 'canvas_http', :path => 'gems/canvas_http'
|
||||
gem 'canvas_mimetype_fu', :path => 'gems/canvas_mimetype_fu'
|
||||
gem 'canvas_sanitize', :path => 'gems/canvas_sanitize'
|
||||
gem 'canvas_statsd', :path => 'gems/canvas_statsd'
|
||||
|
|
|
@ -763,7 +763,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
def validated_turnitin_host(input_host)
|
||||
if input_host.present?
|
||||
_, turnitin_uri = CustomValidations.validate_url(input_host)
|
||||
_, turnitin_uri = CanvasHttp.validate_url(input_host)
|
||||
turnitin_uri.host
|
||||
else
|
||||
nil
|
||||
|
|
|
@ -61,7 +61,7 @@ class ExternalContentController < ApplicationController
|
|||
endpoint = params[:endpoint]
|
||||
url = params[:url]
|
||||
uri = URI.parse(endpoint + (endpoint.match(/\?/) ? '&url=' : '?url=') + CGI.escape(url) + '&format=json')
|
||||
res = Canvas::HTTP.get(uri.to_s) rescue '{}'
|
||||
res = CanvasHttp.get(uri.to_s) rescue '{}'
|
||||
data = JSON.parse(res.body) rescue {}
|
||||
if data['type']
|
||||
if data['type'] == 'photo' && data['url'].try(:match, /^http/)
|
||||
|
|
|
@ -846,7 +846,7 @@ class Account < ActiveRecord::Base
|
|||
return if self.settings[:auth_discovery_url].blank?
|
||||
|
||||
begin
|
||||
value, uri = CustomValidations.validate_url(self.settings[:auth_discovery_url])
|
||||
value, uri = CanvasHttp.validate_url(self.settings[:auth_discovery_url])
|
||||
self.auth_discovery_url = value
|
||||
rescue URI::InvalidURIError, ArgumentError
|
||||
errors.add(:discovery_url, t('errors.invalid_discovery_url', "The discovery URL is not valid" ))
|
||||
|
|
|
@ -1749,7 +1749,7 @@ class Attachment < ActiveRecord::Base
|
|||
|
||||
def clone_url(url, duplicate_handling, check_quota, opts={})
|
||||
begin
|
||||
Canvas::HTTP.clone_url_as_attachment(url, :attachment => self)
|
||||
Attachment.clone_url_as_attachment(url, :attachment => self)
|
||||
|
||||
if check_quota
|
||||
self.save! # save to calculate attachment size, otherwise self.size is nil
|
||||
|
@ -1765,11 +1765,11 @@ class Attachment < ActiveRecord::Base
|
|||
self.file_state = 'errored'
|
||||
self.workflow_state = 'errored'
|
||||
case e
|
||||
when Canvas::HTTP::TooManyRedirectsError
|
||||
when CanvasHttp::TooManyRedirectsError
|
||||
self.upload_error_message = t :upload_error_too_many_redirects, "Too many redirects"
|
||||
when Canvas::HTTP::InvalidResponseCodeError
|
||||
when CanvasHttp::InvalidResponseCodeError
|
||||
self.upload_error_message = t :upload_error_invalid_response_code, "Invalid response code, expected 200 got %{code}", :code => e.code
|
||||
when CustomValidations::RelativeUriError
|
||||
when CanvasHttp::RelativeUriError
|
||||
self.upload_error_message = t :upload_error_relative_uri, "No host provided for the URL: %{url}", :url => url
|
||||
when URI::InvalidURIError, ArgumentError
|
||||
# assigning all ArgumentError to InvalidUri may be incorrect
|
||||
|
@ -1825,4 +1825,38 @@ class Attachment < ActiveRecord::Base
|
|||
def can_unpublish?
|
||||
false
|
||||
end
|
||||
|
||||
# Download a URL using a GET request and return a new un-saved Attachment
|
||||
# with the data at that URL. Tries to detect the correct content_type as
|
||||
# well.
|
||||
#
|
||||
# This handles large files well.
|
||||
#
|
||||
# Pass an existing attachment in opts[:attachment] to use that, rather than
|
||||
# creating a new attachment.
|
||||
def self.clone_url_as_attachment(url, opts = {})
|
||||
_, uri = CanvasHttp.validate_url(url)
|
||||
|
||||
CanvasHttp.get(url) do |http_response|
|
||||
if http_response.code.to_i == 200
|
||||
tmpfile = CanvasHttp.tempfile_for_uri(uri)
|
||||
# net/http doesn't make this very obvious, but read_body can take any
|
||||
# object that responds to << as the destination of the body, and it'll
|
||||
# stream in chunks rather than reading the whole body into memory (as
|
||||
# long as you use the block form of http.request, which
|
||||
# CanvasHttp.get does)
|
||||
http_response.read_body(tmpfile)
|
||||
tmpfile.rewind
|
||||
attachment = opts[:attachment] || Attachment.new(:filename => File.basename(uri.path))
|
||||
attachment.filename ||= File.basename(uri.path)
|
||||
attachment.uploaded_data = tmpfile
|
||||
if attachment.content_type.blank? || attachment.content_type == "unknown/unknown"
|
||||
attachment.content_type = http_response.content_type
|
||||
end
|
||||
return attachment
|
||||
else
|
||||
raise CanvasHttp::InvalidResponseCodeError.new(http_response.code.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,7 +99,7 @@ class ContextExternalTool < ActiveRecord::Base
|
|||
def validate_vendor_help_link
|
||||
return if self.vendor_help_link.blank?
|
||||
begin
|
||||
value, uri = CustomValidations.validate_url(self.vendor_help_link)
|
||||
value, uri = CanvasHttp.validate_url(self.vendor_help_link)
|
||||
self.vendor_help_link = uri.to_s
|
||||
rescue URI::InvalidURIError, ArgumentError
|
||||
self.vendor_help_link = nil
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in canvas_http.gemspec
|
||||
gemspec
|
|
@ -0,0 +1 @@
|
|||
require "bundler/gem_tasks"
|
|
@ -0,0 +1,21 @@
|
|||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "canvas_http"
|
||||
spec.version = "1.0.0"
|
||||
spec.authors = ["Brian Palmer"]
|
||||
spec.email = ["brianp@instructure.com"]
|
||||
spec.summary = %q{Canvas HTTP}
|
||||
|
||||
spec.files = Dir.glob("{lib,spec}/**/*") + %w(Rakefile test.sh)
|
||||
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 1.5"
|
||||
spec.add_development_dependency "rake"
|
||||
spec.add_development_dependency "rspec"
|
||||
spec.add_development_dependency "webmock"
|
||||
end
|
|
@ -0,0 +1,82 @@
|
|||
require 'uri'
|
||||
|
||||
module CanvasHttp
|
||||
class Error < ::Exception; end
|
||||
class TooManyRedirectsError < CanvasHttp::Error; end
|
||||
class InvalidResponseCodeError < CanvasHttp::Error
|
||||
attr_reader :code
|
||||
def initialize(code)
|
||||
super()
|
||||
@code = code
|
||||
end
|
||||
end
|
||||
class RelativeUriError < ArgumentError; end
|
||||
|
||||
# Use this helper method to do HTTP GET requests. It knows how to handle
|
||||
# HTTPS urls, and follows redirects to a given depth.
|
||||
#
|
||||
# Returns the Net::HTTPResponse object, not just the raw response body. If a
|
||||
# block is passed in, the response will also be yielded to the block without
|
||||
# the body having been read yet -- this allows for streaming the response
|
||||
# rather than reading it all into memory.
|
||||
#
|
||||
# Eventually it may be expanded to optionally do cert verification as well.
|
||||
#
|
||||
# TODO: this doesn't yet handle relative redirects (relative Location HTTP
|
||||
# header), which actually isn't even technically allowed by the HTTP spec.
|
||||
# But everybody allows and handles it.
|
||||
def self.get(url_str, other_headers = {}, redirect_limit = 3)
|
||||
loop do
|
||||
raise(TooManyRedirectsError) if redirect_limit <= 0
|
||||
|
||||
_, uri = CanvasHttp.validate_url(url_str)
|
||||
request = Net::HTTP::Get.new(uri.request_uri, other_headers)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = uri.scheme == 'https'
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
http.request(request) do |response|
|
||||
case response
|
||||
when Net::HTTPRedirection
|
||||
url_str = response['Location']
|
||||
redirect_limit -= 1
|
||||
else
|
||||
if block_given?
|
||||
yield response
|
||||
else
|
||||
response.body
|
||||
end
|
||||
return response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# returns [normalized_url_string, URI] if valid, raises otherwise
|
||||
def self.validate_url(value)
|
||||
value = value.strip
|
||||
raise ArgumentError if value.empty?
|
||||
uri = URI.parse(value)
|
||||
unless uri.scheme
|
||||
value = "http://#{value}"
|
||||
uri = URI.parse(value)
|
||||
end
|
||||
raise ArgumentError unless %w(http https).include?(uri.scheme.downcase)
|
||||
raise(RelativeUriError) if uri.host.nil? || uri.host.strip.empty?
|
||||
return value, uri
|
||||
end
|
||||
|
||||
# returns a tempfile with a filename based on the uri (same extension, if
|
||||
# there was an extension)
|
||||
def self.tempfile_for_uri(uri)
|
||||
basename = File.basename(uri.path)
|
||||
basename, ext = basename.split(".", 2)
|
||||
tmpfile = if ext
|
||||
Tempfile.new([basename, ext])
|
||||
else
|
||||
Tempfile.new(basename)
|
||||
end
|
||||
tmpfile.set_encoding(Encoding::BINARY) if tmpfile.respond_to?(:set_encoding)
|
||||
tmpfile.binmode
|
||||
tmpfile
|
||||
end
|
||||
end
|
|
@ -0,0 +1,77 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
require 'webmock'
|
||||
|
||||
describe "CanvasHttp" do
|
||||
|
||||
include WebMock::API
|
||||
|
||||
describe ".get" do
|
||||
it "should return response objects" do
|
||||
stub_request(:get, "http://www.example.com/a/b").
|
||||
to_return(body: "Hello", headers: { 'Content-Length' => 5 })
|
||||
res = CanvasHttp.get("http://www.example.com/a/b")
|
||||
res.should be_a Net::HTTPOK
|
||||
res.body.should == "Hello"
|
||||
end
|
||||
|
||||
it "does not use ssl" do
|
||||
http = double.as_null_object
|
||||
Net::HTTP.stub(:new) { http }
|
||||
expect(http).to receive(:use_ssl=).with(false)
|
||||
response = double('Response')
|
||||
expect(response).to receive(:body)
|
||||
expect(http).to receive(:request).and_yield(response)
|
||||
|
||||
CanvasHttp.get("http://www.example.com/a/b")
|
||||
end
|
||||
|
||||
it "should use ssl" do
|
||||
http = double
|
||||
Net::HTTP.stub(:new) { http }
|
||||
expect(http).to receive(:use_ssl=).with(true)
|
||||
expect(http).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
expect(http).to receive(:request).and_yield(double(body: 'Hello SSL'))
|
||||
|
||||
CanvasHttp.get("https://www.example.com/a/b").body.should == "Hello SSL"
|
||||
end
|
||||
|
||||
it "should follow redirects" do
|
||||
stub_request(:get, "http://www.example.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example2.com/a'})
|
||||
stub_request(:get, "http://www.example2.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example3.com/a'})
|
||||
stub_request(:get, "http://www.example3.com/a").
|
||||
to_return(body: "Hello", headers: { 'Content-Length' => 5 })
|
||||
res = CanvasHttp.get("http://www.example.com/a")
|
||||
res.should be_a Net::HTTPOK
|
||||
res.body.should == "Hello"
|
||||
end
|
||||
|
||||
it "should fail on too many redirects" do
|
||||
stub_request(:get, "http://www.example.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example2.com/a'})
|
||||
stub_request(:get, "http://www.example2.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example3.com/a'})
|
||||
expect { CanvasHttp.get("http://www.example.com/a", {}, 2) }.to raise_error(CanvasHttp::TooManyRedirectsError)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
require 'canvas_http'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.filter_run :focus
|
||||
config.color_enabled true
|
||||
|
||||
config.order = 'random'
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
result=0
|
||||
|
||||
bundle install
|
||||
bundle exec rspec spec
|
||||
let result=$result+$?
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
echo "SUCCESS"
|
||||
else
|
||||
echo "FAILURE"
|
||||
fi
|
||||
|
||||
exit $result
|
|
@ -30,7 +30,7 @@ module AppCenter
|
|||
response = Rails.cache.fetch(cache_key, :expires_in => expires) do
|
||||
uri = URI.parse("#{base_url}#{endpoint}")
|
||||
uri.query = [uri.query, "offset=#{offset}"].compact.join('&')
|
||||
Canvas::HTTP.get(uri.to_s).body
|
||||
CanvasHttp.get(uri.to_s).body
|
||||
end
|
||||
|
||||
json = JSON.parse(response)
|
||||
|
@ -128,7 +128,7 @@ module AppCenter
|
|||
uri.query = URI.encode_www_form(params)
|
||||
uri.to_s
|
||||
|
||||
response = Canvas::HTTP.get(uri.to_s).body
|
||||
response = CanvasHttp.get(uri.to_s).body
|
||||
json = JSON.parse(response)
|
||||
json = json['reviews'].first if json['reviews']
|
||||
rescue
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
require 'uri'
|
||||
|
||||
module Canvas::HTTP
|
||||
class Error < ::Exception; end
|
||||
class TooManyRedirectsError < Canvas::HTTP::Error; end
|
||||
class InvalidResponseCodeError < Canvas::HTTP::Error
|
||||
attr_reader :code
|
||||
def initialize(code)
|
||||
super()
|
||||
@code = code
|
||||
end
|
||||
end
|
||||
|
||||
# Use this helper method to do HTTP GET requests. It knows how to handle
|
||||
# HTTPS urls, and follows redirects to a given depth.
|
||||
#
|
||||
# Returns the Net::HTTPResponse object, not just the raw response body. If a
|
||||
# block is passed in, the response will also be yielded to the block without
|
||||
# the body having been read yet -- this allows for streaming the response
|
||||
# rather than reading it all into memory.
|
||||
#
|
||||
# Eventually it may be expanded to optionally do cert verification as well.
|
||||
#
|
||||
# TODO: this doesn't yet handle relative redirects (relative Location HTTP
|
||||
# header), which actually isn't even technically allowed by the HTTP spec.
|
||||
# But everybody allows and handles it.
|
||||
def self.get(url_str, other_headers = {}, redirect_limit = 3)
|
||||
loop do
|
||||
raise(TooManyRedirectsError) if redirect_limit <= 0
|
||||
|
||||
url, uri = CustomValidations.validate_url(url_str)
|
||||
request = Net::HTTP::Get.new(uri.request_uri, other_headers)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true if uri.scheme == 'https'
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
http.request(request) do |response|
|
||||
case response
|
||||
when Net::HTTPRedirection
|
||||
url_str = response['Location']
|
||||
redirect_limit -= 1
|
||||
else
|
||||
if block_given?
|
||||
yield response
|
||||
else
|
||||
response.body
|
||||
end
|
||||
return response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Download a URL using a GET request and return a new un-saved Attachment
|
||||
# with the data at that URL. Tries to detect the correct content_type as
|
||||
# well.
|
||||
#
|
||||
# This handles large files well.
|
||||
#
|
||||
# Pass an existing attachment in opts[:attachment] to use that, rather than
|
||||
# creating a new attachment.
|
||||
def self.clone_url_as_attachment(url, opts = {})
|
||||
_, uri = CustomValidations.validate_url(url)
|
||||
|
||||
Canvas::HTTP.get(url) do |http_response|
|
||||
if http_response.code.to_i == 200
|
||||
tmpfile = tempfile_for_uri(uri)
|
||||
# net/http doesn't make this very obvious, but read_body can take any
|
||||
# object that responds to << as the destination of the body, and it'll
|
||||
# stream in chunks rather than reading the whole body into memory (as
|
||||
# long as you use the block form of http.request, which
|
||||
# Canvas::HTTP.get does)
|
||||
http_response.read_body(tmpfile)
|
||||
tmpfile.rewind
|
||||
attachment = opts[:attachment] || Attachment.new(:filename => File.basename(uri.path))
|
||||
attachment.filename ||= File.basename(uri.path)
|
||||
attachment.uploaded_data = tmpfile
|
||||
if attachment.content_type.blank? || attachment.content_type == "unknown/unknown"
|
||||
attachment.content_type = http_response.content_type
|
||||
end
|
||||
return attachment
|
||||
else
|
||||
raise InvalidResponseCodeError.new(http_response.code.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# returns a tempfile with a filename based on the uri (same extension, if
|
||||
# there was an extension)
|
||||
def self.tempfile_for_uri(uri)
|
||||
basename = File.basename(uri.path)
|
||||
basename, ext = basename.split(".", 2)
|
||||
tmpfile = if ext
|
||||
Tempfile.new([basename, ext])
|
||||
else
|
||||
Tempfile.new(basename)
|
||||
end
|
||||
tmpfile.set_encoding(Encoding::BINARY) if tmpfile.respond_to?(:set_encoding)
|
||||
tmpfile.binmode
|
||||
tmpfile
|
||||
end
|
||||
end
|
|
@ -17,28 +17,15 @@
|
|||
#
|
||||
|
||||
module CustomValidations
|
||||
class RelativeUriError < ArgumentError; end
|
||||
|
||||
# returns [normalized_url_string, URI] if valid, raises otherwise
|
||||
def self.validate_url(value)
|
||||
value = value.strip
|
||||
raise ArgumentError if value.empty?
|
||||
uri = URI.parse(value)
|
||||
unless uri.scheme
|
||||
value = "http://#{value}"
|
||||
uri = URI.parse(value)
|
||||
end
|
||||
raise(RelativeUriError) if uri.host.blank?
|
||||
raise ArgumentError unless %w(http https).include?(uri.scheme.downcase)
|
||||
return value, uri
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def validates_as_url(*fields)
|
||||
validates_each(fields, :allow_nil => true) do |record, attr, value|
|
||||
begin
|
||||
value, uri = CustomValidations.validate_url(value)
|
||||
value, uri = CanvasHttp.validate_url(value)
|
||||
|
||||
record.send("#{attr}=", value)
|
||||
rescue URI::InvalidURIError, ArgumentError
|
||||
record.errors.add attr, 'is not a valid URL'
|
||||
|
|
|
@ -244,7 +244,7 @@ module Kaltura
|
|||
data = {}
|
||||
data[:result] = result
|
||||
url = result.css('logFileUrl')[0].content
|
||||
csv = CSV.parse(Canvas::HTTP.get(url).body)
|
||||
csv = CSV.parse(CanvasHttp.get(url).body)
|
||||
data[:entries] = []
|
||||
csv.each do |row|
|
||||
data[:entries] << {
|
||||
|
|
|
@ -141,7 +141,7 @@ shared_examples_for "file uploads api" do
|
|||
json = api_call(:get, status_url, {:id => attachment.id.to_s, :controller => 'files', :action => 'api_file_status', :format => 'json', :uuid => attachment.uuid})
|
||||
json['upload_status'].should == 'pending'
|
||||
|
||||
Canvas::HTTP.expects(:get).with("http://www.example.com/images/delete.png").yields(FakeHttpResponse.new(200, "asdf"))
|
||||
CanvasHttp.expects(:get).with("http://www.example.com/images/delete.png").yields(FakeHttpResponse.new(200, "asdf"))
|
||||
run_download_job
|
||||
|
||||
json = api_call(:get, status_url, {:id => attachment.id.to_s, :controller => 'files', :action => 'api_file_status', :format => 'json', :uuid => attachment.uuid})
|
||||
|
@ -194,7 +194,7 @@ shared_examples_for "file uploads api" do
|
|||
|
||||
local_storage!
|
||||
# step 1, preflight
|
||||
Canvas::HTTP.expects(:get).with(url).yields(FakeHttpResponse.new(404))
|
||||
CanvasHttp.expects(:get).with(url).yields(FakeHttpResponse.new(404))
|
||||
json = preflight({ :name => filename, :size => 20, :url => url })
|
||||
attachment = Attachment.order(:id).last
|
||||
json['status_url'].should == "http://www.example.com/api/v1/files/#{attachment.id}/#{attachment.uuid}/status"
|
||||
|
@ -213,7 +213,7 @@ shared_examples_for "file uploads api" do
|
|||
|
||||
local_storage!
|
||||
# step 1, preflight
|
||||
Canvas::HTTP.expects(:get).with(url).raises(Timeout::Error)
|
||||
CanvasHttp.expects(:get).with(url).raises(Timeout::Error)
|
||||
json = preflight({ :name => filename, :size => 20, :url => url })
|
||||
attachment = Attachment.order(:id).last
|
||||
json['status_url'].should == "http://www.example.com/api/v1/files/#{attachment.id}/#{attachment.uuid}/status"
|
||||
|
@ -232,7 +232,7 @@ shared_examples_for "file uploads api" do
|
|||
|
||||
local_storage!
|
||||
# step 1, preflight
|
||||
Canvas::HTTP.expects(:get).with(url).raises(Canvas::HTTP::TooManyRedirectsError)
|
||||
CanvasHttp.expects(:get).with(url).raises(CanvasHttp::TooManyRedirectsError)
|
||||
json = preflight({ :name => filename, :size => 20, :url => url })
|
||||
attachment = Attachment.order(:id).last
|
||||
attachment.workflow_state.should == 'unattached'
|
||||
|
@ -312,7 +312,7 @@ shared_examples_for "file uploads api with folders" do
|
|||
a1 = Attachment.create!(:folder => @folder, :context => context, :filename => "test.txt", :uploaded_data => StringIO.new("first"))
|
||||
json = preflight({ :name => "test.txt", :folder => "test", :url => "http://www.example.com/test" })
|
||||
attachment = Attachment.order(:id).last
|
||||
Canvas::HTTP.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, "second"))
|
||||
CanvasHttp.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, "second"))
|
||||
run_jobs
|
||||
|
||||
a1.reload.should be_deleted
|
||||
|
@ -349,7 +349,7 @@ shared_examples_for "file uploads api with folders" do
|
|||
a1 = Attachment.create!(:folder => @folder, :context => context, :filename => "test.txt", :uploaded_data => StringIO.new("first"))
|
||||
json = preflight({ :name => "test.txt", :folder => "test", :on_duplicate => 'rename', :url => "http://www.example.com/test" })
|
||||
attachment = Attachment.order(:id).last
|
||||
Canvas::HTTP.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, "second"))
|
||||
CanvasHttp.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, "second"))
|
||||
run_jobs
|
||||
|
||||
a1.reload.should be_available
|
||||
|
@ -461,7 +461,7 @@ shared_examples_for "file uploads api with quotas" do
|
|||
json = preflight({ :name => "test.txt", :url => "http://www.example.com/test" })
|
||||
status_url = json['status_url']
|
||||
attachment = Attachment.order(:id).last
|
||||
Canvas::HTTP.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, (" " * 2.megabytes)))
|
||||
CanvasHttp.expects(:get).with("http://www.example.com/test").yields(FakeHttpResponse.new(200, (" " * 2.megabytes)))
|
||||
run_jobs
|
||||
|
||||
json = api_call(:get, status_url, {:id => attachment.id.to_s, :controller => 'files', :action => 'api_file_status', :format => 'json', :uuid => attachment.uuid})
|
||||
|
|
|
@ -56,20 +56,20 @@ describe AppCenter::AppApi do
|
|||
endpoint = '/?myparam=value'
|
||||
per_page = 11
|
||||
page = 3
|
||||
Canvas::HTTP.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{page * per_page - per_page}").returns(response)
|
||||
CanvasHttp.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{page * per_page - per_page}").returns(response)
|
||||
api.fetch_app_center_response(endpoint, 11.minutes, page, per_page)
|
||||
end
|
||||
|
||||
it "can handle an invalid response" do
|
||||
response.stubs(:body).returns('')
|
||||
Canvas::HTTP.expects(:get).returns(response)
|
||||
CanvasHttp.expects(:get).returns(response)
|
||||
api.fetch_app_center_response('', 12.minutes, 8, 3).should == {}
|
||||
end
|
||||
|
||||
it "can handle an error response" do
|
||||
message = {"message" => "Tool not found", "type" => "error"}
|
||||
response.stubs(:body).returns(message.to_json)
|
||||
Canvas::HTTP.expects(:get).returns(response)
|
||||
CanvasHttp.expects(:get).returns(response)
|
||||
api.fetch_app_center_response('', 13.minutes, 6, 9).should == message
|
||||
end
|
||||
|
||||
|
@ -78,7 +78,7 @@ describe AppCenter::AppApi do
|
|||
per_page = 1
|
||||
page = 1
|
||||
offset = page * per_page - per_page
|
||||
Canvas::HTTP.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{offset}").returns(response)
|
||||
CanvasHttp.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{offset}").returns(response)
|
||||
response = api.fetch_app_center_response(endpoint, 11.minutes, page, per_page)
|
||||
results = response['objects']
|
||||
results.size.should == 1
|
||||
|
@ -93,7 +93,7 @@ describe AppCenter::AppApi do
|
|||
per_page = 5
|
||||
page = 1
|
||||
offset = page * per_page - per_page
|
||||
Canvas::HTTP.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{offset}").returns(response)
|
||||
CanvasHttp.expects(:get).with("#{api.app_center.settings['base_url']}#{endpoint}&offset=#{offset}").returns(response)
|
||||
response = api.fetch_app_center_response(endpoint, 11.minutes, page, per_page)
|
||||
results = response['objects']
|
||||
results.size.should == 4
|
||||
|
@ -104,7 +104,7 @@ describe AppCenter::AppApi do
|
|||
it "resets the cache when getting an invalid response" do
|
||||
enable_cache do
|
||||
response.stubs(:body).returns('')
|
||||
Canvas::HTTP.expects(:get).returns(response).twice()
|
||||
CanvasHttp.expects(:get).returns(response).twice()
|
||||
api.fetch_app_center_response('', 13.minutes, 7, 4).should == {}
|
||||
api.fetch_app_center_response('', 13.minutes, 7, 4).should == {}
|
||||
end
|
||||
|
@ -112,7 +112,7 @@ describe AppCenter::AppApi do
|
|||
|
||||
it "uses the configured token as part of the cache key" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.expects(:get).returns(response).twice()
|
||||
CanvasHttp.expects(:get).returns(response).twice()
|
||||
|
||||
api.fetch_app_center_response('/endpoint/url', 13.minutes, 7, 4)
|
||||
|
||||
|
@ -152,14 +152,14 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "gets a list of apps" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
apps = api.get_apps()['lti_apps']
|
||||
apps.should be_a Array
|
||||
apps.size.should == 2
|
||||
end
|
||||
|
||||
it "returns an empty hash if the app center is disabled" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
setting = PluginSetting.find_by_name(api.app_center.id)
|
||||
setting.destroy
|
||||
|
||||
|
@ -172,7 +172,7 @@ describe AppCenter::AppApi do
|
|||
|
||||
it "gets the next page" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
response = api.get_apps
|
||||
response['meta']['next_page'].should == 2
|
||||
end
|
||||
|
@ -180,7 +180,7 @@ describe AppCenter::AppApi do
|
|||
|
||||
it "caches apps results" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.expects(:get).returns(response).once
|
||||
CanvasHttp.expects(:get).returns(response).once
|
||||
api.get_apps()
|
||||
api.get_apps()
|
||||
end
|
||||
|
@ -188,7 +188,7 @@ describe AppCenter::AppApi do
|
|||
|
||||
it "caches multiple calls" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.expects(:get).returns(response).times(2)
|
||||
CanvasHttp.expects(:get).returns(response).times(2)
|
||||
api.get_apps(0)
|
||||
api.get_apps(1)
|
||||
api.get_apps(0)
|
||||
|
@ -261,7 +261,7 @@ describe AppCenter::AppApi do
|
|||
}
|
||||
|
||||
response.stubs(:body).returns({"objects" => [app]}.to_json)
|
||||
Canvas::HTTP.expects(:get).returns(response)
|
||||
CanvasHttp.expects(:get).returns(response)
|
||||
json = api.get_apps(0)
|
||||
tool = json['lti_apps'].first
|
||||
tool['short_name'].should == app['id']
|
||||
|
@ -344,7 +344,7 @@ describe AppCenter::AppApi do
|
|||
]
|
||||
}
|
||||
response.stubs(:body).returns({"lti_apps" => [app]}.to_json)
|
||||
Canvas::HTTP.expects(:get).returns(response)
|
||||
CanvasHttp.expects(:get).returns(response)
|
||||
json = api.get_apps(0)
|
||||
tool = json['lti_apps'].first
|
||||
|
||||
|
@ -375,7 +375,7 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "gets an apps user review" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
review = api.get_app_user_review('first_tool', 12345)
|
||||
review.should be_a Hash
|
||||
review['rating'].should == 3
|
||||
|
@ -383,7 +383,7 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "returns an empty hash if the app center is disabled" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
setting = PluginSetting.find_by_name(api.app_center.id)
|
||||
setting.destroy
|
||||
|
||||
|
@ -509,14 +509,14 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "gets an apps reviews" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
reviews = api.get_app_reviews('first_tool')['reviews']
|
||||
reviews.should be_a Array
|
||||
reviews.size.should == 2
|
||||
end
|
||||
|
||||
it "returns an empty hash if the app center is disabled" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
setting = PluginSetting.find_by_name(api.app_center.id)
|
||||
setting.destroy
|
||||
|
||||
|
@ -528,14 +528,14 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "gets the next page" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
response = api.get_app_reviews('first_tool')
|
||||
response['meta']['next_page'].should == 2
|
||||
end
|
||||
|
||||
it "caches apps results" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.expects(:get).returns(response)
|
||||
CanvasHttp.expects(:get).returns(response)
|
||||
api.get_app_reviews('first_tool')
|
||||
api.get_app_reviews('first_tool')
|
||||
end
|
||||
|
@ -543,7 +543,7 @@ describe AppCenter::AppApi do
|
|||
|
||||
it "caches multiple calls" do
|
||||
enable_cache do
|
||||
Canvas::HTTP.expects(:get).returns(response).times(2)
|
||||
CanvasHttp.expects(:get).returns(response).times(2)
|
||||
api.get_app_reviews('first_tool', 0)
|
||||
api.get_app_reviews('first_tool', 1)
|
||||
api.get_app_reviews('first_tool', 0)
|
||||
|
@ -552,7 +552,7 @@ describe AppCenter::AppApi do
|
|||
end
|
||||
|
||||
it "can handle an edu-apps api v1 response" do
|
||||
Canvas::HTTP.stubs(:get).returns(response)
|
||||
CanvasHttp.stubs(:get).returns(response)
|
||||
reviews = api.get_app_reviews('first_tool')['reviews']
|
||||
reviews.first.should be_key('user')
|
||||
reviews.first['user'].should be_key('name')
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
require_webmock
|
||||
|
||||
describe "Canvas::HTTP" do
|
||||
|
||||
include WebMock::API
|
||||
|
||||
before do
|
||||
WebMock.enable!
|
||||
end
|
||||
|
||||
after do
|
||||
WebMock.reset!
|
||||
WebMock.disable!
|
||||
end
|
||||
|
||||
describe ".get" do
|
||||
it "should return response objects" do
|
||||
http_stub = Net::HTTP.any_instance
|
||||
http_stub.expects(:use_ssl=).never
|
||||
stub_request(:get, "http://www.example.com/a/b").
|
||||
to_return(body: "Hello", headers: { 'Content-Length' => 5 })
|
||||
res = Canvas::HTTP.get("http://www.example.com/a/b")
|
||||
res.should be_a Net::HTTPOK
|
||||
res.body.should == "Hello"
|
||||
end
|
||||
|
||||
it "should use ssl" do
|
||||
http_stub = Net::HTTP.any_instance
|
||||
res = mock('response', :body => 'test')
|
||||
http_stub.expects(:use_ssl=).with(true)
|
||||
http_stub.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
||||
http_stub.expects(:request).yields(res)
|
||||
Canvas::HTTP.get("https://www.example.com/a/b").should == res
|
||||
end
|
||||
|
||||
it "should follow redirects" do
|
||||
stub_request(:get, "http://www.example.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example2.com/a'})
|
||||
stub_request(:get, "http://www.example2.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example3.com/a'})
|
||||
stub_request(:get, "http://www.example3.com/a").
|
||||
to_return(body: "Hello", headers: { 'Content-Length' => 5 })
|
||||
res = Canvas::HTTP.get("http://www.example.com/a")
|
||||
res.should be_a Net::HTTPOK
|
||||
res.body.should == "Hello"
|
||||
end
|
||||
|
||||
it "should fail on too many redirects" do
|
||||
stub_request(:get, "http://www.example.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example2.com/a'})
|
||||
stub_request(:get, "http://www.example2.com/a").
|
||||
to_return(status: 301, headers: { 'Location' => 'http://www.example3.com/a'})
|
||||
expect { Canvas::HTTP.get("http://www.example.com/a", {}, 2) }.to raise_error(Canvas::HTTP::TooManyRedirectsError)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".clone_url_as_attachment" do
|
||||
it "should reject invalid urls" do
|
||||
expect { Canvas::HTTP.clone_url_as_attachment("ftp://some/stuff") }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should not raise on non-200 responses" do
|
||||
url = "http://example.com/test.png"
|
||||
Canvas::HTTP.expects(:get).with(url).yields(stub('code' => '401'))
|
||||
expect { Canvas::HTTP.clone_url_as_attachment(url) }.to raise_error(Canvas::HTTP::InvalidResponseCodeError)
|
||||
end
|
||||
|
||||
it "should use an existing attachment if passed in" do
|
||||
url = "http://example.com/test.png"
|
||||
a = attachment_model
|
||||
Canvas::HTTP.expects(:get).with(url).yields(FakeHttpResponse.new('200', 'this is a jpeg', 'content-type' => 'image/jpeg'))
|
||||
Canvas::HTTP.clone_url_as_attachment(url, :attachment => a)
|
||||
a.save!
|
||||
a.open.read.should == "this is a jpeg"
|
||||
end
|
||||
|
||||
it "should detect the content_type from the body" do
|
||||
url = "http://example.com/test.png"
|
||||
Canvas::HTTP.expects(:get).with(url).yields(FakeHttpResponse.new('200', 'this is a jpeg', 'content-type' => 'image/jpeg'))
|
||||
att = Canvas::HTTP.clone_url_as_attachment(url)
|
||||
att.should be_present
|
||||
att.should be_new_record
|
||||
att.content_type.should == 'image/jpeg'
|
||||
att.context = Account.default
|
||||
att.save!
|
||||
att.open.read.should == 'this is a jpeg'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1641,6 +1641,39 @@ describe Attachment do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".clone_url_as_attachment" do
|
||||
it "should reject invalid urls" do
|
||||
expect { Attachment.clone_url_as_attachment("ftp://some/stuff") }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should not raise on non-200 responses" do
|
||||
url = "http://example.com/test.png"
|
||||
CanvasHttp.expects(:get).with(url).yields(stub('code' => '401'))
|
||||
expect { Attachment.clone_url_as_attachment(url) }.to raise_error(CanvasHttp::InvalidResponseCodeError)
|
||||
end
|
||||
|
||||
it "should use an existing attachment if passed in" do
|
||||
url = "http://example.com/test.png"
|
||||
a = attachment_model
|
||||
CanvasHttp.expects(:get).with(url).yields(FakeHttpResponse.new('200', 'this is a jpeg', 'content-type' => 'image/jpeg'))
|
||||
Attachment.clone_url_as_attachment(url, :attachment => a)
|
||||
a.save!
|
||||
a.open.read.should == "this is a jpeg"
|
||||
end
|
||||
|
||||
it "should detect the content_type from the body" do
|
||||
url = "http://example.com/test.png"
|
||||
CanvasHttp.expects(:get).with(url).yields(FakeHttpResponse.new('200', 'this is a jpeg', 'content-type' => 'image/jpeg'))
|
||||
att = Attachment.clone_url_as_attachment(url)
|
||||
att.should be_present
|
||||
att.should be_new_record
|
||||
att.content_type.should == 'image/jpeg'
|
||||
att.context = Account.default
|
||||
att.save!
|
||||
att.open.read.should == 'this is a jpeg'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def processing_model
|
||||
|
|
Loading…
Reference in New Issue