├── .gitignore ├── .travis.yml ├── BENCHMARKS.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── VERSION ├── examples ├── cache.rb ├── report.rb └── simple.rb ├── lib ├── ext │ └── hash.rb ├── maxcdn.rb └── maxcdn │ └── version.rb ├── maxcdn.gemspec └── test ├── benchmark.rb ├── ext_hash_test.rb ├── integration.rb └── maxcdn_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | 4 | # rdoc generated 5 | rdoc 6 | 7 | # yard generated 8 | doc 9 | .yardoc 10 | 11 | # bundler 12 | .bundle 13 | vendor 14 | 15 | # jeweler generated 16 | pkg 17 | *.gem 18 | .DS_Store 19 | npm-debug.log 20 | 21 | # other 22 | *.env 23 | .vendor/ 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | script: bundle exec rake test 3 | sudo: false 4 | before_install: 5 | - gem update bundler 6 | rvm: 7 | - 2.3 8 | - 2.4 9 | -------------------------------------------------------------------------------- /BENCHMARKS.md: -------------------------------------------------------------------------------- 1 | Running on Ruby version: 2 | 3 | ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux] 4 | 5 | 6 | Running benchmarks as follows in order: 7 | 8 | maxcdn.get('zones/pull.json') 9 | maxcdn.get('reports/popularfiles.json') 10 | maxcdn.get('v3/reporting/logs.json') 11 | maxcdn.post('zones/pull.json', { :name => 'NAM', :url => 'URL' }) 12 | maxcdn.put('account.json', { :name => 'NAME' }) 13 | maxcdn.delete('zones/pull.json/ZONEID') 14 | maxcdn.purge('ZONEID') 15 | maxcdn.purge('ZONEID', 'FILE') 16 | maxcdn.purge('ZONEID', [ 'FILE1','FILE2' ]) 17 | 18 | user system total real 19 | get : 0.010000 0.000000 0.010000 ( 0.444660) 20 | get : 0.000000 0.000000 0.000000 ( 0.180768) 21 | get : 0.000000 0.010000 0.010000 ( 0.556110) 22 | post : 0.010000 0.000000 0.010000 ( 3.665597) 23 | put : 0.000000 0.000000 0.000000 ( 0.370504) 24 | delete: 0.010000 0.000000 0.010000 ( 2.557845) 25 | purge : 0.000000 0.000000 0.000000 ( 1.392236) 26 | purge : 0.000000 0.000000 0.000000 ( 1.386664) 27 | purge : 0.000000 0.000000 0.000000 ( 2.841199) 28 | 29 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :development, :test do 6 | gem "rake" 7 | gem "pry" 8 | gem "minitest" 9 | gem "minitest-reporters" 10 | gem "webmock" 11 | end 12 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | maxcdn (0.4.0) 5 | addressable (~> 2.4) 6 | faraday (~> 0.9) 7 | net-http-persistent (~> 2.9) 8 | signet (~> 0.7) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | addressable (2.5.2) 14 | public_suffix (>= 2.0.2, < 4.0) 15 | ansi (1.5.0) 16 | builder (3.2.3) 17 | coderay (1.1.2) 18 | crack (0.4.3) 19 | safe_yaml (~> 1.0.0) 20 | faraday (0.15.0) 21 | multipart-post (>= 1.2, < 3) 22 | hashdiff (0.3.7) 23 | jwt (2.1.0) 24 | method_source (0.9.0) 25 | minitest (5.11.3) 26 | minitest-reporters (1.2.0) 27 | ansi 28 | builder 29 | minitest (>= 5.0) 30 | ruby-progressbar 31 | multi_json (1.13.1) 32 | multipart-post (2.0.0) 33 | net-http-persistent (2.9.4) 34 | pry (0.11.3) 35 | coderay (~> 1.1.0) 36 | method_source (~> 0.9.0) 37 | public_suffix (3.0.2) 38 | rake (12.3.1) 39 | ruby-progressbar (1.9.0) 40 | safe_yaml (1.0.4) 41 | signet (0.8.1) 42 | addressable (~> 2.3) 43 | faraday (~> 0.9) 44 | jwt (>= 1.5, < 3.0) 45 | multi_json (~> 1.10) 46 | webmock (3.4.1) 47 | addressable (>= 2.3.6) 48 | crack (>= 0.3.2) 49 | hashdiff 50 | 51 | PLATFORMS 52 | ruby 53 | 54 | DEPENDENCIES 55 | maxcdn! 56 | minitest 57 | minitest-reporters 58 | pry 59 | rake 60 | webmock 61 | 62 | BUNDLED WITH 63 | 1.16.1 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 David Litvak Bruno 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MaxCDN REST Web Services Ruby Client 2 | 3 | [![Build Status](https://travis-ci.org/MaxCDN/ruby-maxcdn.png?branch=master)](https://travis-ci.org/MaxCDN/ruby-maxcdn)   [![Gem Version](https://badge.fury.io/rb/maxcdn.png)](http://badge.fury.io/rb/maxcdn) 4 | 5 | ## Installation 6 | 7 | ``` bash 8 | gem install maxcdn 9 | ``` 10 | 11 | > Requires Ruby 2.3+ (see: [Travis](https://travis-ci.org/MaxCDN/ruby-maxcdn) for passing Ruby versions.) 12 | 13 | #### With Bundler 14 | 15 | ``` 16 | bundle init 17 | echo "gem 'maxcdn'" >> Gemfile 18 | bundle install --path vendor/bundle 19 | ``` 20 | 21 | ## Usage 22 | ```ruby 23 | require 'maxcdn' 24 | 25 | api = MaxCDN::Client.new("myalias", "consumer_key", "consumer_secret") 26 | 27 | #### 28 | # Turn on debugging outputs 29 | # 30 | # api.debug = true 31 | 32 | api.get("/account.json") 33 | ``` 34 | 35 | ## Methods 36 | It has support for `GET`, `POST`, `PUT` and `DELETE` OAuth 1.0a signed requests. 37 | 38 | ```ruby 39 | # To create a new Pull Zone 40 | api.post("/zones/pull.json", {'name' => 'test_zone', 'url' => 'http://my-test-site.com'}) 41 | 42 | # To update an existing zone 43 | api.put("/zones/pull.json/1234", {'name' => 'i_didnt_like_test'}) 44 | 45 | # To delete a zone 46 | api.delete("/zones/pull.json/1234") 47 | 48 | # To purge a file (robots.txt) from cache 49 | api.delete("/zones/pull.json/1234/cache", {"file" => "/robots.txt"}) 50 | ``` 51 | 52 | ### We now have a shortcut for Purge Calls! 53 | ```ruby 54 | zone_id = 12345 55 | 56 | # Purge Zone 57 | api.purge(zone_id) 58 | 59 | # Purge File 60 | api.purge(zone_id, '/some_file') 61 | 62 | # Purge Files 63 | api.purge(zone_id, ['/some_file', '/another_file']) 64 | ``` 65 | 66 | #### Example: SSL Upload 67 | 68 | ``` 69 | max = MaxCDN::Client.new(alias, key, secret) 70 | max.post("zones/pull/12345/ssl.json", { 71 | :ssl_crt => File.read("/path/to/server.crt").strip, 72 | :ssl_key => File.read("/path/to/server.key").strip }) 73 | ``` 74 | 75 | ## Development Quick Start 76 | 77 | ``` bash 78 | # get it 79 | git clone git@github.com:/ruby-maxcdn.git 80 | 81 | # setup 82 | cd ruby-maxcdn 83 | bundle install --path vendor/bundle 84 | 85 | # unit tests 86 | bundle exec ruby ./test/test.rb 87 | 88 | # integration tests 89 | export ALIAS= 90 | export KEY= 91 | export SECRET= 92 | bundle exec ruby ./test/integration.rb # requires host's IP be whitelisted 93 | ``` 94 | 95 | # Change Log 96 | 97 | ##### 0.3.0 98 | 99 | * Replace CurbFu with Faraday (#10). 100 | 101 | ##### 0.2.1 102 | 103 | * Upgrade signet gem to allow for use with the faraday 0.9.x series (#7). 104 | * See https://rubygems.org/gems/maxcdn/versions/0.2.1 for gem. 105 | 106 | ##### 0.1.5 107 | 108 | * Issue #4: Fixing purge files by array to return a hash of status results. 109 | 110 | ##### 0.1.4 111 | 112 | * Fixing bug where purging files purges entire zone. 113 | 114 | ##### 0.1.3 115 | 116 | * Requested changes for debugging and development support. (See issue #2). 117 | 118 | ##### 0.1.2 119 | 120 | * Fixing an issue with lib loading in `0.1.1`. 121 | 122 | ##### 0.1.1 123 | 124 | * Fixing POST, DELETE and PUT to send data via request body. 125 | * Adding debugging for CurbFu and Curl::Easy. 126 | * Fixing/enhancing unit tests. 127 | * Removing `secure_connection` handling, as all connections should be secure. 128 | * Fixing [414 Request-URI Too Large](https://github.com/netdna/netdnarws-ruby/issues/10) from old [netdnarws-ruby](https://github.com/netdna/netdnarws-ruby) client. 129 | 130 | 131 | ##### 0.1.0 132 | 133 | * Initial Release 134 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require "rake/testtask" 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs << "test" 7 | t.test_files = FileList['test/*_test.rb'] 8 | t.verbose = true 9 | end 10 | 11 | Rake::TestTask.new(:integration) do |t| 12 | t.libs << "test" 13 | t.test_files = FileList['test/integration.rb'] 14 | t.verbose = true 15 | end 16 | 17 | desc "Run benchmarks" 18 | task :benchmark do 19 | require "./test/benchmark" 20 | end 21 | 22 | task :default => :test 23 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.4 2 | -------------------------------------------------------------------------------- /examples/cache.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "pp" 3 | require File.join("./", File.dirname(__FILE__), "..","lib","maxcdn") 4 | 5 | zoneid = ARGV.shift 6 | files = ARGV.clone 7 | 8 | if ENV["ALIAS"].nil? or ENV["KEY"].nil? or ENV["SECRET"].nil? 9 | puts <<-EOU 10 | Usage: cache.rb zoneid [files...] 11 | 12 | Add credentials to your environment like so: 13 | 14 | $ export ALIAS= 15 | $ export KEY= 16 | $ export SECRET= 17 | $ bundle exec ruby cache.rb 12345 ./master.css ./another.css 18 | 19 | Or by adding to the script call: 20 | 21 | $ ALIAS= KEY= SECRET= bundle exec ruby cache.rb \ 22 | 12345 ./master.css ./another.css 23 | EOU 24 | end 25 | 26 | maxcdn = MaxCDN::Client.new(ENV["ALIAS"], ENV["KEY"], ENV["SECRET"]) 27 | 28 | if zoneid.nil? 29 | 30 | maxcdn.get("/zones/pull.json")["data"]["pullzones"].map { |z| z["id"] }.each do |zid| 31 | puts "Purging zone #{zid}" 32 | pp maxcdn.purge(zid) 33 | end 34 | 35 | else 36 | 37 | puts "Purging zone #{zoneid}" 38 | if files.empty? 39 | pp maxcdn.purge(zoneid) 40 | else 41 | pp maxcdn.purge(zoneid, files) 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /examples/report.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "pp" 3 | require File.join("./", File.dirname(__FILE__), "..","lib","maxcdn") 4 | 5 | arg = ARGV.shift 6 | 7 | if ENV["ALIAS"].nil? or ENV["KEY"].nil? or ENV["SECRET"].nil? or (!arg.nil? and !arg.match(/hourly|daily|monthly/)) 8 | puts <<-EOU 9 | Usage: report.rb [hourly|daily|monthly] 10 | 11 | Report types only cover summary. 12 | 13 | Add credentials to your environment like so: 14 | 15 | $ export ALIAS= 16 | $ export KEY= 17 | $ export SECRET= 18 | $ ./report.rb 19 | 20 | Or by adding to the script call: 21 | 22 | $ ALIAS= KEY= SECRET= ./report.rb 23 | EOU 24 | end 25 | 26 | maxcdn = MaxCDN::Client.new(ENV["ALIAS"], ENV["KEY"], ENV["SECRET"]) 27 | 28 | report = arg.nil? ? "" : "/#{arg}" 29 | 30 | maxcdn.get("/zones/pull.json")["data"]["pullzones"].each do |zone| 31 | # title 32 | puts "Zone report for: #{zone["name"]} (#{zone["url"]})" 33 | 34 | # summary 35 | maxcdn.get("/reports/#{zone["id"]}/stats.json#{report}")["data"]["summary"].each do |k, v| 36 | puts " - #{k}: #{v}" 37 | end 38 | 39 | # popularfiles 40 | puts " " 41 | puts "Popular Files:" 42 | maxcdn.get("/reports/#{zone["id"]}/popularfiles.json?page_size=10")["data"]["popularfiles"].each do |file| 43 | puts " - url: #{file["uri"]}" 44 | puts " - hits: #{file["hit"]}" 45 | puts " - size: #{file["size"]}" 46 | end 47 | 48 | puts " " 49 | end 50 | 51 | -------------------------------------------------------------------------------- /examples/simple.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pp' 3 | require File.join('./', File.dirname(__FILE__), '..','lib','maxcdn') 4 | 5 | maxcdn = MaxCDN::Client.new(ENV['ALIAS'], ENV['KEY'], ENV['SECRET']) 6 | 7 | puts 'GET /account.json' 8 | pp maxcdn.get('/account.json') 9 | 10 | puts 'GET /account.json/address' 11 | pp maxcdn.get('/account.json/address') 12 | 13 | puts 'GET /reports/stats.json/hourly' 14 | pp maxcdn.get('/reports/stats.json/hourly') 15 | 16 | -------------------------------------------------------------------------------- /lib/ext/hash.rb: -------------------------------------------------------------------------------- 1 | require 'addressable' 2 | 3 | class Hash 4 | def _escape(value) 5 | Addressable::URI.encode(Addressable::URI.parse(value.to_s)) 6 | rescue ArgumentError 7 | Addressable::URI.encode(Addressable::URI.parse(value.to_s.force_encoding(Encoding::UTF_8))) 8 | end 9 | 10 | def to_params 11 | params = '' 12 | stack = [] 13 | 14 | each do |k, v| 15 | if v.is_a?(Hash) 16 | stack << [k,v] 17 | elsif v.is_a?(Array) 18 | stack << [k,Hash.from_array(v)] 19 | else 20 | params << "#{_escape(k)}=#{_escape(v)}&" 21 | end 22 | end 23 | 24 | stack.each do |parent, hash| 25 | parent = _escape(parent) if parent.is_a? String 26 | hash.each do |k, v| 27 | if v.is_a?(Hash) 28 | stack << ["#{parent}[#{k}]", v] 29 | else 30 | params << "#{parent}[#{k}]=#{_escape(v)}&" 31 | end 32 | end 33 | end 34 | 35 | params.chop! 36 | params 37 | end 38 | 39 | def case_indifferent_delete key 40 | existing_keys = self.keys.map { |k| k.downcase } 41 | index = existing_keys.index(key.downcase) 42 | unless index.nil? 43 | self.delete(self.keys[index]) 44 | end 45 | end 46 | 47 | def case_indifferent_merge incoming_hash 48 | existing_keys = self.keys.map { |k| k.downcase } 49 | 50 | incoming_hash.each do |key, value| 51 | index = existing_keys.index(key.downcase) 52 | if index.nil? 53 | self[key] = value 54 | else 55 | self[self.keys[index]] = value 56 | end 57 | end 58 | end 59 | 60 | def self.from_array(array = []) 61 | h = Hash.new 62 | array.size.times do |t| 63 | h[t] = array[t] 64 | end 65 | h 66 | end 67 | end 68 | 69 | -------------------------------------------------------------------------------- /lib/maxcdn.rb: -------------------------------------------------------------------------------- 1 | require "signet/oauth_1/client" 2 | require "json" 3 | require "ext/hash" 4 | require "pp" # for debug 5 | 6 | module MaxCDN 7 | class APIException < StandardError 8 | end 9 | 10 | class Client 11 | attr_accessor :client, :debug 12 | def initialize(company_alias, key, secret, server="rws.maxcdn.com", secure_connection=true, _debug=false) 13 | @debug = _debug 14 | @secure_connection = secure_connection 15 | @company_alias = company_alias 16 | @server = server 17 | @request_signer = Signet::OAuth1::Client.new( 18 | client_credential_key: key, 19 | client_credential_secret: secret, 20 | two_legged: true 21 | ) 22 | end 23 | 24 | def _connection_type 25 | @secure_connection ? "https" : "http" 26 | end 27 | 28 | def _get_url uri, params={} 29 | url = "#{_connection_type}://#{@server}/#{@company_alias}/#{uri.gsub(/^\//, "")}" 30 | url += "?#{params.to_params}" if params && !params.empty? 31 | url 32 | end 33 | 34 | def _response_as_json method, uri, options={}, data={} 35 | puts "Making #{method.upcase} request to #{_get_url uri}" if debug 36 | 37 | req_opts = { method: method } 38 | 39 | req_opts[:uri] = _get_url(uri, (options[:body] ? {} : data)) 40 | req_opts[:body] = data.to_params if options[:body] 41 | 42 | request = @request_signer.generate_authenticated_request(req_opts) 43 | 44 | # crazyness for headers 45 | headers = { 46 | "Content-Type" => options[:body] ? "application/json" : "application/x-www-form-urlencoded" 47 | } 48 | 49 | headers.case_indifferent_merge(options.delete(:headers) || {}) 50 | headers["User-Agent"] = "Ruby MaxCDN API Client" 51 | 52 | # merge headers with request headers 53 | request.headers.case_indifferent_merge(headers) 54 | 55 | begin 56 | url, path = req_opts[:uri].match(%r"^(https://[^/]+)(/.+)$")[1, 2] 57 | conn = Faraday.new(:url => url) do |faraday| 58 | faraday.adapter :net_http_persistent 59 | end 60 | 61 | response = conn.send(method, path) do |req| 62 | req.headers = request.headers 63 | req.body = request.body 64 | end 65 | 66 | return response if options[:debug_request] 67 | pp response if debug 68 | 69 | response_json = JSON.load(response.body) 70 | return response_json if options[:debug_json] 71 | pp response_json if debug 72 | 73 | unless response.success? 74 | error_message = response_json["error"]["message"] 75 | raise MaxCDN::APIException.new("#{response.status}: #{error_message}") 76 | end 77 | rescue TypeError 78 | raise MaxCDN::APIException.new("#{response.status}: No information supplied by the server") 79 | end 80 | 81 | response_json 82 | end 83 | 84 | [ :post, :put ].each do |method| 85 | define_method(method) do |uri, data={}, options={}| 86 | options[:body] ||= true 87 | self._response_as_json method.to_s, uri, options, data 88 | end 89 | end 90 | 91 | [ :get, :delete ].each do |method| 92 | define_method(method) do |uri, data={}, options={}| 93 | options[:body] = false 94 | self._response_as_json method.to_s, uri, options, data 95 | end 96 | end 97 | 98 | def purge zone_id, file_or_files=nil, options={} 99 | if file_or_files.nil? 100 | return self.delete("/zones/pull.json/#{zone_id}/cache", {}, options) 101 | end 102 | 103 | if file_or_files.is_a?(String) 104 | return self.delete("/zones/pull.json/#{zone_id}/cache", { "files" => file_or_files }, options) 105 | end 106 | 107 | if file_or_files.is_a?(Array) 108 | result = {} 109 | file_or_files.each do |file| 110 | result[file] = self.delete("/zones/pull.json/#{zone_id}/cache", { "files" => file }, options) 111 | end 112 | return result 113 | end 114 | 115 | if file_or_files.is_a?(Hash) 116 | return self.purge(zone_id, file_or_files[:files]) if file_or_files.has_key?("files") 117 | return self.purge(zone_id, file_or_files[:files]) if file_or_files.has_key?(:files) 118 | end 119 | 120 | raise MaxCDN::APIException.new("Invalid file_or_files argument for delete method.") 121 | end 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /lib/maxcdn/version.rb: -------------------------------------------------------------------------------- 1 | module MaxCDN 2 | VERSION = "0.4.0" 3 | end 4 | -------------------------------------------------------------------------------- /maxcdn.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/maxcdn/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.name = "maxcdn" 6 | gem.homepage = "http://www.maxcdn.com" 7 | gem.version = MaxCDN::VERSION 8 | gem.license = "MIT" 9 | gem.files = `git ls-files`.split($\) 10 | gem.require_paths = ['lib'] 11 | gem.summary = %Q{A Rest Client For MaxCDN Rest Web Services} 12 | gem.description = %Q{A Rest Client For MaxCDN Rest Web Services} 13 | gem.email = "joshua@mervine.net" 14 | gem.authors = ["Joshua P. Mervine"] 15 | gem.add_dependency 'signet', '~> 0.7' 16 | gem.add_dependency 'faraday', '~> 0.9' 17 | gem.add_dependency 'net-http-persistent', '~> 2.9' 18 | gem.add_dependency 'addressable', '~> 2.4' 19 | end 20 | -------------------------------------------------------------------------------- /test/benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | if (ENV["ALIAS"].nil? or ENV["KEY"].nil? or ENV["SECRET"].nil?) 3 | abort "Please export ALIAS, KEY and SECRET with your credentials and ensure that you're test host's IP is whitelisted." 4 | end 5 | 6 | require 'benchmark' 7 | require "./lib/maxcdn" 8 | 9 | @time = Time.now.to_i.to_s 10 | 11 | puts "Running benchmarks as follows in order:" 12 | puts " " 13 | puts " maxcdn.get('zones/pull.json')" 14 | puts " maxcdn.get('reports/popularfiles.json')" 15 | puts " maxcdn.get('v3/reporting/logs.json')" 16 | puts " maxcdn.post('zones/pull.json', { :name => 'NAM', :url => 'URL' })" 17 | puts " maxcdn.put('account.json', { :name => 'NAME' })" 18 | puts " maxcdn.delete('zones/pull.json/ZONEID')" 19 | puts " maxcdn.purge('ZONEID')" 20 | puts " maxcdn.purge('ZONEID', 'FILE')" 21 | puts " maxcdn.purge('ZONEID', [ 'FILE1','FILE2' ])" 22 | puts " " 23 | 24 | Benchmark.bm do |mark| 25 | 26 | maxcdn = MaxCDN::Client.new(ENV["ALIAS"], ENV["KEY"], ENV["SECRET"]) 27 | 28 | mark.report("get :") do 29 | zones = maxcdn.get("zones/pull.json")["data"]["pullzones"] 30 | @pullzone = zones[zones.length-1]["id"] 31 | end 32 | 33 | mark.report("get :") do 34 | @popularfiles = maxcdn.get("reports/popularfiles.json")["data"]["popularfiles"] 35 | end 36 | 37 | mark.report("get :") do 38 | @popularfiles = maxcdn.get("v3/reporting/logs.json")["next_page_key"] 39 | end 40 | 41 | @zone = { 42 | :name => @time, 43 | :url => "http://www.example.com" 44 | } 45 | 46 | mark.report("post :") do 47 | @zoneid = maxcdn.post("zones/pull.json", @zone)["data"]["pullzone"]["id"] 48 | end 49 | 50 | mark.report("put :") do 51 | maxcdn.put("account.json", { :name => @time }) 52 | end 53 | 54 | mark.report("delete:") do 55 | maxcdn.delete("zones/pull.json/#{@zoneid}") 56 | end 57 | 58 | mark.report("purge :") do 59 | maxcdn.purge(@pullzone) 60 | end 61 | 62 | mark.report("purge :") do 63 | maxcdn.purge(@pullzone, @popularfiles[0]["uri"]) 64 | end 65 | 66 | mark.report("purge :") do 67 | maxcdn.purge(@pullzone, [ @popularfiles[0]["uri"], @popularfiles[1]["uri"] ]) 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /test/ext_hash_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "minitest/autorun" 3 | require "minitest/reporters" 4 | 5 | require "./lib/ext/hash" 6 | 7 | class Client < Minitest::Test 8 | def test_to_params_basic 9 | assert_equal "foo=bar", { "foo" => "bar" }.to_params 10 | assert_equal "foo=bar&bar=foo", { :foo => "bar", :bar => "foo" }.to_params 11 | end 12 | 13 | def test_to_params_escape 14 | assert_equal "foo%20bar=bar%20boo", { "foo bar" => "bar boo" }.to_params 15 | end 16 | 17 | def test_to_params_array 18 | assert_equal "foo[0]=bar&foo[1]=boo", { "foo" => [ "bar", "boo" ] }.to_params 19 | assert_equal "foo[0]=bar&foo[1]=boo", { :foo => [ :bar, :boo ] }.to_params 20 | end 21 | 22 | def test_case_indifferent_delete 23 | original_hash = { 24 | # using realistic content types to test real use cases 25 | "content-type" => "1", 26 | "X-Forwarded-For" => "2", 27 | "CACHE-CONTROL" => "3" 28 | } 29 | 30 | assert_equal "1", original_hash.case_indifferent_delete("CONTENT-TYPE"), "delete 'CONTENT-TYPE'" 31 | refute original_hash.has_key?("content-type"), "deleted 'content-type'" 32 | 33 | assert_equal "3", original_hash.case_indifferent_delete("cache-control"), "delete 'cache-control'" 34 | refute original_hash.has_key?("CACHE-CONTROL"), "delete 'CACHE-CONTROL'" 35 | end 36 | 37 | def test_case_indifferent_merge 38 | original_hash = { 39 | # using realistic content types to test real use cases 40 | "content-type" => "1", 41 | "X-Forwarded-For" => "2", 42 | "CACHE-CONTROL" => "3", 43 | "unchanged-original" => "4" 44 | } 45 | 46 | new_hash = { 47 | # using realistic content types to test real use cases 48 | "Content-Type" => "5", 49 | "X-FORWARDED-FOR" => "6", 50 | "cache-control" => "7", 51 | "new-header" => "8" 52 | } 53 | 54 | assert original_hash.case_indifferent_merge(new_hash) 55 | 56 | assert_equal "4", original_hash["unchanged-original"] ,"unchanged-original" 57 | assert_equal "5", original_hash["content-type"] ,"content-type" 58 | assert_equal "6", original_hash["X-Forwarded-For"] ,"X-Forwarded-For" 59 | assert_equal "7", original_hash["CACHE-CONTROL"] ,"CACHE-CONTROL" 60 | assert_equal "8", original_hash["new-header"] ,"new-header" 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /test/integration.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "json" 3 | require "minitest/autorun" 4 | require "minitest/reporters" 5 | require "./lib/maxcdn" 6 | 7 | Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new 8 | 9 | if (ENV["ALIAS"].nil? or ENV["KEY"].nil? or ENV["SECRET"].nil?) 10 | abort "Please export ALIAS, KEY and SECRET with your credentials and ensure that you're test host's IP is whitelisted." 11 | end 12 | 13 | class Client < Minitest::Test 14 | 15 | def setup 16 | @max = MaxCDN::Client.new(ENV["ALIAS"], ENV["KEY"], ENV["SECRET"]) 17 | @max.debug = true if ENV['DEBUG'] 18 | 19 | @time = Time.now.to_i.to_s 20 | end 21 | 22 | def test_get 23 | [ "account.json", 24 | "account.json/address", 25 | "users.json", 26 | "zones.json" 27 | ].each do |end_point| 28 | key = end_point.include?("/") ? end_point.split("/")[1] : end_point.gsub(/\.json/, "") 29 | 30 | assert @max.get(end_point)["data"][key], "get #{key} with data" 31 | end 32 | end 33 | 34 | def test_get_logs 35 | assert @max.get("v3/reporting/logs.json")["next_page_key"], "get next_page_key with data" 36 | end 37 | 38 | def test_post_and_delete 39 | 40 | zone = { 41 | :name => @time, 42 | :url => "http://www.example.com" 43 | } 44 | 45 | zid = @max.post("zones/pull.json", zone)["data"]["pullzone"]["id"] 46 | assert zid, "post id" 47 | 48 | assert_equal 200, @max.delete("zones/pull.json/#{zid}")["code"], "delete (warning: manually delete zone #{zid} at https://cp.maxcdn.com/zones/pull)." 49 | end 50 | 51 | def test_put 52 | name = @time + "_put" 53 | assert_equal name, @max.put("account.json", { :name => name })["data"]["account"]["name"], "put" 54 | end 55 | 56 | def test_purge 57 | zones = @max.get("zones/pull.json")["data"]["pullzones"] 58 | zone = zones[zones.length-1]["id"] 59 | assert_equal 200, @max.purge(zone)["code"], "purge" 60 | 61 | popularfiles = @max.get("reports/popularfiles.json")["data"]["popularfiles"] 62 | assert_equal 200, @max.purge(zone, popularfiles[0]["uri"])["code"], "purge file" 63 | 64 | files = [popularfiles[0]["uri"], popularfiles[1]["uri"]] 65 | expected = {files[0]=>{"code"=>200},files[1]=>{"code"=>200}} 66 | assert_equal expected, @max.purge(zone, files), "purge files" 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /test/maxcdn_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "minitest/autorun" 3 | require "minitest/reporters" 4 | 5 | require "./lib/maxcdn" 6 | 7 | Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new 8 | 9 | require "webmock" 10 | include WebMock::API 11 | WebMock.enable! 12 | 13 | host = "https://rws.maxcdn.com/alias" 14 | 15 | expected_headers = { 16 | 'Authorization' => /.+/, 17 | 'Cache-Control' => /.+/, 18 | 'Content-Type' => "application/json", 19 | 'User-Agent' => "Ruby MaxCDN API Client" 20 | } 21 | 22 | # requests with :body 23 | stub_request(:post, host+"/zones/pull.json") 24 | .with(:body => "foo=bar&bar=foo", :headers => expected_headers) 25 | .to_return(:body => '{"foo": "bar"}') 26 | 27 | stub_request(:put, host+"/account.json") 28 | .with(:body => "foo=bar", :headers => expected_headers) 29 | .to_return(:body => '{"foo":"bar"}') 30 | 31 | # requests without :body 32 | expected_headers['Content-Type'] = "application/x-www-form-urlencoded" 33 | stub_request(:get, host+"/account.json") 34 | .with(:headers => expected_headers) 35 | .to_return(:body => '{"foo":"bar"}') 36 | 37 | stub_request(:delete, host+"/zones/pull.json/12345/cache?files=foo.txt") 38 | .with(:headers => expected_headers) 39 | .to_return(:body => '{"foo":"bar"}') 40 | 41 | stub_request(:delete, host+"/zones/pull.json/12345/cache?files=bar.txt") 42 | .with(:headers => expected_headers) 43 | .to_return(:body => '{"foo":"bar"}') 44 | 45 | stub_request(:delete, host+"/zones/pull.json/12345/cache") 46 | .with(:headers => expected_headers) 47 | .to_return(:body => '{"foo":"bar"}') 48 | 49 | # test custom content-type 50 | expected_headers['Content-Type'] = "application/custom" 51 | stub_request(:get, host+"/account.json/address") 52 | .with(:headers => expected_headers) 53 | .to_return(:body => '{"foo":"bar"}') 54 | 55 | # ingore headers 56 | stub_request(:post, host+"/zones/pull.json?foo=bar") 57 | .to_return(:body => '{"foo": "bar"}') 58 | 59 | class Client < Minitest::Test 60 | 61 | def setup 62 | @max = MaxCDN::Client.new("alias", "key", "secret") 63 | @max.debug = true if ENV['DEBUG'] 64 | end 65 | 66 | def test_initialize 67 | assert_equal "alias", @max.instance_variable_get(:@company_alias) 68 | end 69 | 70 | def test__connection_type 71 | assert_equal "https", @max._connection_type 72 | 73 | max = MaxCDN::Client.new("alias", "key", "secret", "rws.maxcdn.com", false) 74 | assert_equal "http", max._connection_type 75 | end 76 | 77 | def test__get_url 78 | assert_equal "https://rws.maxcdn.com/alias/foo", 79 | @max._get_url("/foo") 80 | assert_equal "https://rws.maxcdn.com/alias/foo?foo=foo%20bar", 81 | @max._get_url("/foo", { :foo => "foo bar" }) 82 | end 83 | 84 | def test__response_as_json_standard 85 | res = @max._response_as_json("get", "account.json") 86 | assert_equal({ "foo" => "bar" }, res) 87 | 88 | res = @max._response_as_json("post", "zones/pull.json?foo=bar") 89 | assert_equal({ "foo" => "bar" }, res) 90 | end 91 | 92 | def test__response_as_json_body 93 | res = @max._response_as_json("post", "zones/pull.json", { :body => true }, { "foo"=> "bar", "bar" => "foo" }) 94 | assert_equal({ "foo" => "bar" }, res) 95 | end 96 | 97 | def test__response_as_json_debug_json 98 | res = @max._response_as_json("post", "zones/pull.json", { :body => true, :debug_json => true }, { "foo"=> "bar", "bar" => "foo" }) 99 | assert_equal({ "foo" => "bar" }, res) 100 | end 101 | 102 | def test__response_as_json_debug_request 103 | res = @max._response_as_json("post", "zones/pull.json", { :body => true, :debug_request => true }, { "foo"=> "bar", "bar" => "foo" }) 104 | assert_equal(Faraday::Response, res.class) 105 | end 106 | 107 | def test_custom_header 108 | assert_equal({ "foo" => "bar" }, @max.get("account.json/address", {}, { :headers => { 'content-type' => 'application/custom' }})) 109 | end 110 | 111 | def test_get 112 | assert_equal({ "foo" => "bar" }, @max.get("account.json")) 113 | end 114 | 115 | def test_post 116 | assert_equal({ "foo" => "bar" }, @max.post("zones/pull.json", {"foo" => "bar", "bar" => "foo"})) 117 | assert_equal({ "foo" => "bar" }, @max.post("zones/pull.json?foo=bar")) 118 | end 119 | 120 | def test_put 121 | assert_equal({ "foo" => "bar" }, @max.put("account.json", {"foo"=>"bar"})) 122 | end 123 | 124 | def test_delete_cache 125 | assert_equal({ "foo" => "bar" }, @max.delete("zones/pull.json/12345/cache")) 126 | end 127 | 128 | def test_delete_cache_w_files 129 | assert_equal({ "foo" => "bar" }, @max.delete("zones/pull.json/12345/cache", { :files => "foo.txt" })) 130 | end 131 | 132 | def test_purge 133 | assert_equal({ "foo" => "bar" }, @max.purge(12345)) 134 | end 135 | 136 | def test_purge_file 137 | assert_equal({ "foo" => "bar" }, @max.purge(12345, "foo.txt")) 138 | end 139 | 140 | def test_purge_files 141 | assert_equal({"foo.txt"=>{"foo"=>"bar"}, "bar.txt"=>{"foo"=>"bar"}}, @max.purge(12345, [ "foo.txt", "bar.txt" ])) 142 | assert_equal({"foo.txt"=>{"foo"=>"bar"}, "bar.txt"=>{"foo"=>"bar"}}, @max.purge(12345, { :files => [ "foo.txt", "bar.txt" ]})) 143 | end 144 | end 145 | 146 | --------------------------------------------------------------------------------