awis-sdk-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +13 -0
- data/.travis.yml +9 -0
- data/Gemfile +13 -0
- data/README.md +149 -0
- data/Rakefile +11 -0
- data/awis.gemspec +31 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/awis.rb +19 -0
- data/lib/awis/api.rb +10 -0
- data/lib/awis/api/base.rb +26 -0
- data/lib/awis/api/category_browse.rb +36 -0
- data/lib/awis/api/category_listings.rb +45 -0
- data/lib/awis/api/sites_linking_in.rb +29 -0
- data/lib/awis/api/traffic_history.rb +30 -0
- data/lib/awis/api/url_info.rb +33 -0
- data/lib/awis/client.rb +44 -0
- data/lib/awis/config.rb +15 -0
- data/lib/awis/connection.rb +90 -0
- data/lib/awis/exceptions.rb +16 -0
- data/lib/awis/hash.rb +9 -0
- data/lib/awis/models.rb +10 -0
- data/lib/awis/models/base.rb +26 -0
- data/lib/awis/models/category_browse.rb +123 -0
- data/lib/awis/models/category_listings.rb +53 -0
- data/lib/awis/models/sites_linking_in.rb +40 -0
- data/lib/awis/models/traffic_history.rb +59 -0
- data/lib/awis/models/url_info.rb +206 -0
- data/lib/awis/utils/extra.rb +11 -0
- data/lib/awis/utils/xml.rb +34 -0
- data/lib/awis/version.rb +3 -0
- metadata +159 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Awis
|
2
|
+
module API
|
3
|
+
class UrlInfo < Base
|
4
|
+
DEFAULT_RESPONSE_GROUP = %w(related_links categories rank rank_by_country usage_stats adult_content speed language owned_domains links_in_count site_data).freeze
|
5
|
+
|
6
|
+
def fetch(arguments = {})
|
7
|
+
raise ArgumentError, "Any valid URL." unless arguments.has_key?(:url)
|
8
|
+
@arguments = arguments
|
9
|
+
@arguments[:response_group] = Array(arguments.fetch(:response_group, DEFAULT_RESPONSE_GROUP))
|
10
|
+
|
11
|
+
loading_response_data
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def loading_response_data
|
16
|
+
@response_body = Awis::Connection.new.get(params)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def params
|
21
|
+
{
|
22
|
+
"Action" => action_name,
|
23
|
+
"Url" => arguments[:url],
|
24
|
+
"ResponseGroup" => response_groups,
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def response_groups
|
29
|
+
arguments[:response_group].sort.map { |group| camelize(group) }.join(",")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/awis/client.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Awis
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
raise CertificateError.new("Amazon access certificate is missing!") if Awis.config.access_key_id.nil? || Awis.config.secret_access_key.nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
def url_info(args)
|
9
|
+
parse_response_with_request("UrlInfo", args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def traffic_history(args)
|
13
|
+
parse_response_with_request("TrafficHistory", args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def sites_linking_in(args)
|
17
|
+
parse_response_with_request("SitesLinkingIn", args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def category_browse(args)
|
21
|
+
parse_response_with_request("CategoryBrowse", args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def category_listings(args)
|
25
|
+
parse_response_with_request("CategoryListings", args)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def parse_response_with_request(kclass, args)
|
30
|
+
case kclass
|
31
|
+
when 'UrlInfo'
|
32
|
+
Awis::Models::UrlInfo.new Awis::API::UrlInfo.new.fetch(args)
|
33
|
+
when 'SitesLinkingIn'
|
34
|
+
Awis::Models::SitesLinkingIn.new Awis::API::SitesLinkingIn.new.fetch(args)
|
35
|
+
when 'TrafficHistory'
|
36
|
+
Awis::Models::TrafficHistory.new Awis::API::TrafficHistory.new.fetch(args)
|
37
|
+
when 'CategoryBrowse'
|
38
|
+
Awis::Models::CategoryBrowse.new Awis::API::CategoryBrowse.new.fetch(args)
|
39
|
+
when 'CategoryListings'
|
40
|
+
Awis::Models::CategoryListings.new Awis::API::CategoryListings.new.fetch(args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/awis/config.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "base64"
|
3
|
+
require "openssl"
|
4
|
+
require "digest/sha1"
|
5
|
+
require "faraday"
|
6
|
+
require "time"
|
7
|
+
|
8
|
+
module Awis
|
9
|
+
class Connection
|
10
|
+
attr_accessor :access_key_id, :secret_access_key, :proxy, :debug
|
11
|
+
attr_writer :params
|
12
|
+
|
13
|
+
RFC_3986_UNRESERVED_CHARS = "-_.~a-zA-Z\\d".freeze
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
raise CertificateError.new("Amazon access certificate is missing!") if Awis.config.access_key_id.nil? || Awis.config.secret_access_key.nil?
|
17
|
+
|
18
|
+
@access_key_id = Awis.config.access_key_id
|
19
|
+
@secret_access_key = Awis.config.secret_access_key
|
20
|
+
@debug = Awis.config.debug || nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def params
|
24
|
+
@params ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(params = {})
|
28
|
+
self.params = params
|
29
|
+
|
30
|
+
handle_response(request).body.force_encoding(Encoding::UTF_8)
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_response(response)
|
34
|
+
case response.status.to_i
|
35
|
+
when 200...300
|
36
|
+
response
|
37
|
+
when 300...600
|
38
|
+
if response.body.nil?
|
39
|
+
raise ResponseError.new(nil, response)
|
40
|
+
else
|
41
|
+
xml = MultiXml.parse(response.body)
|
42
|
+
message = xml["Response"]["Errors"]["Error"]["Message"]
|
43
|
+
raise ResponseError.new(message, response)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise ResponseError.new("Unknown code: #{respnse.code}", response)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def request
|
51
|
+
puts "[DEBUG] -> #{uri}" if debug
|
52
|
+
|
53
|
+
Faraday.get(uri)
|
54
|
+
end
|
55
|
+
|
56
|
+
def timestamp
|
57
|
+
@timestamp ||= Time::now.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
58
|
+
end
|
59
|
+
|
60
|
+
def signature
|
61
|
+
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), secret_access_key, sign)).strip
|
62
|
+
end
|
63
|
+
|
64
|
+
def uri
|
65
|
+
URI.parse("http://#{Awis::API_HOST}/?" + query + "&Signature=" + CGI::escape(signature))
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_params
|
69
|
+
{
|
70
|
+
"AWSAccessKeyId" => access_key_id,
|
71
|
+
"SignatureMethod" => "HmacSHA256",
|
72
|
+
"SignatureVersion" => "2",
|
73
|
+
"Timestamp" => timestamp,
|
74
|
+
"Version" => Awis::API_VERSION
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def sign
|
79
|
+
"GET\n" + Awis::API_HOST + "\n/\n" + query
|
80
|
+
end
|
81
|
+
|
82
|
+
def query
|
83
|
+
default_params.merge(params).map { |key, value| "#{key}=#{regexp_params(value)}" }.sort.join("&")
|
84
|
+
end
|
85
|
+
|
86
|
+
def regexp_params(value)
|
87
|
+
URI.escape(value.to_s, Regexp.new("[^#{RFC_3986_UNRESERVED_CHARS}]"))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Awis
|
2
|
+
# Awis exceptions can be cought by rescuing: Awis::StandardError
|
3
|
+
|
4
|
+
class StandardError < StandardError; end
|
5
|
+
class ArgumentError < StandardError; end
|
6
|
+
class CertificateError < StandardError; end
|
7
|
+
|
8
|
+
class ResponseError < StandardError
|
9
|
+
attr_reader :response
|
10
|
+
|
11
|
+
def initialize(message, response)
|
12
|
+
@response = response
|
13
|
+
super(message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/awis/hash.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
class Hash
|
2
|
+
def deep_find(key)
|
3
|
+
key?(key) ? self[key] : self.values.inject(nil) { |memo, v| memo ||= v.deep_find(key) if v.respond_to?(:deep_find) }
|
4
|
+
end
|
5
|
+
|
6
|
+
def array_slice_merge!(key, array, count)
|
7
|
+
self[key] = array.each_slice(count).collect { |e| e.reduce({}, :merge) }
|
8
|
+
end
|
9
|
+
end
|
data/lib/awis/models.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Awis
|
2
|
+
module Models
|
3
|
+
autoload :Base, "awis/models/base"
|
4
|
+
autoload :UrlInfo, "awis/models/url_info"
|
5
|
+
autoload :TrafficHistory, "awis/models/traffic_history"
|
6
|
+
autoload :SitesLinkingIn, "awis/models/sites_linking_in"
|
7
|
+
autoload :CategoryListings, "awis/models/category_listings"
|
8
|
+
autoload :CategoryBrowse, "awis/models/category_browse"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Awis
|
2
|
+
module Models
|
3
|
+
class Base
|
4
|
+
attr_accessor :response, :status_code, :request_id
|
5
|
+
|
6
|
+
def loading_response(response)
|
7
|
+
Awis::Utils::XML.new(response.response_body)
|
8
|
+
end
|
9
|
+
|
10
|
+
def root_node_name
|
11
|
+
"/aws:#{action_name}Response/aws:Response/aws:#{action_name}Result/aws:Alexa"
|
12
|
+
end
|
13
|
+
|
14
|
+
def action_name
|
15
|
+
self.class.name.split(/\:\:/)[-1]
|
16
|
+
end
|
17
|
+
|
18
|
+
def relationship_collections(_object, items, items_count, kclass)
|
19
|
+
return if items.empty?
|
20
|
+
|
21
|
+
all_items = {}.array_slice_merge!(:item, items, items_count)
|
22
|
+
all_items.map { |item| _object << kclass.new(item) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Awis
|
2
|
+
module Models
|
3
|
+
class CategoryBrowse < Base
|
4
|
+
attr_accessor :categories, :language_categories, :related_categories, :letter_bars
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@categories = []
|
8
|
+
@language_categories = []
|
9
|
+
@related_categories = []
|
10
|
+
@letter_bars = []
|
11
|
+
|
12
|
+
setup_data!( loading_response(response) )
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_data!(response)
|
16
|
+
categories = []
|
17
|
+
language_categories = []
|
18
|
+
related_categories = []
|
19
|
+
letter_bars = []
|
20
|
+
|
21
|
+
response.each_node do |node, path|
|
22
|
+
text = node.inner_xml
|
23
|
+
text = text.to_i if text.to_i.to_s === text
|
24
|
+
|
25
|
+
if node.name == 'aws:RequestId'
|
26
|
+
@request_id ||= text
|
27
|
+
elsif node.name == 'aws:StatusCode'
|
28
|
+
@status_code ||= text
|
29
|
+
elsif node.name == 'aws:Path' && path == "#{category_node_name}/aws:Path"
|
30
|
+
categories << { path: text }
|
31
|
+
elsif node.name == 'aws:Title' && path == "#{category_node_name}/aws:Title"
|
32
|
+
categories << { title: text }
|
33
|
+
elsif node.name == 'aws:SubCategoryCount' && path == "#{category_node_name}/aws:SubCategoryCount"
|
34
|
+
categories << { sub_category_count: text }
|
35
|
+
elsif node.name == 'aws:TotalListingCount' && path == "#{category_node_name}/aws:TotalListingCount"
|
36
|
+
categories << { total_listing_count: text }
|
37
|
+
elsif node.name == 'aws:Path' && path == "#{language_category_node_name}/aws:Path"
|
38
|
+
language_categories << { path: text }
|
39
|
+
elsif node.name == 'aws:Title' && path == "#{language_category_node_name}/aws:Title"
|
40
|
+
language_categories << { title: text }
|
41
|
+
elsif node.name == 'aws:SubCategoryCount' && path == "#{language_category_node_name}/aws:SubCategoryCount"
|
42
|
+
language_categories << { sub_category_count: text }
|
43
|
+
elsif node.name == 'aws:TotalListingCount' && path == "#{language_category_node_name}/aws:TotalListingCount"
|
44
|
+
language_categories << { total_listing_count: text }
|
45
|
+
elsif node.name == 'aws:Path' && path == "#{related_category_node_name}/aws:Path"
|
46
|
+
related_categories << { path: text }
|
47
|
+
elsif node.name == 'aws:Title' && path == "#{related_category_node_name}/aws:Title"
|
48
|
+
related_categories << { title: text }
|
49
|
+
elsif node.name == 'aws:SubCategoryCount' && path == "#{related_category_node_name}/aws:SubCategoryCount"
|
50
|
+
related_categories << { sub_category_count: text }
|
51
|
+
elsif node.name == 'aws:TotalListingCount' && path == "#{related_category_node_name}/aws:TotalListingCount"
|
52
|
+
related_categories << { total_listing_count: text }
|
53
|
+
elsif node.name == 'aws:Path' && path == "#{letter_bars_node_name}/aws:Path"
|
54
|
+
letter_bars << { path: text }
|
55
|
+
elsif node.name == 'aws:Title' && path == "#{letter_bars_node_name}/aws:Title"
|
56
|
+
letter_bars << { title: text }
|
57
|
+
elsif node.name == 'aws:SubCategoryCount' && path == "#{letter_bars_node_name}/aws:SubCategoryCount"
|
58
|
+
letter_bars << { sub_category_count: text }
|
59
|
+
elsif node.name == 'aws:TotalListingCount' && path == "#{letter_bars_node_name}/aws:TotalListingCount"
|
60
|
+
letter_bars << { total_listing_count: text }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
relationship_collections(@categories, categories, 4, Category)
|
65
|
+
relationship_collections(@language_categories, language_categories, 4, LanguageCategory)
|
66
|
+
relationship_collections(@related_categories, related_categories, 4, RelatedCategory)
|
67
|
+
relationship_collections(@letter_bars, letter_bars, 4, LetterBar)
|
68
|
+
end
|
69
|
+
|
70
|
+
def base_node_name
|
71
|
+
"#{root_node_name}/aws:CategoryBrowse"
|
72
|
+
end
|
73
|
+
|
74
|
+
def category_node_name
|
75
|
+
"#{base_node_name}/aws:Categories/aws:Category"
|
76
|
+
end
|
77
|
+
|
78
|
+
def language_category_node_name
|
79
|
+
"#{base_node_name}/aws:LanguageCategories/aws:Category"
|
80
|
+
end
|
81
|
+
|
82
|
+
def related_category_node_name
|
83
|
+
"#{base_node_name}/aws:RelatedCategories/aws:Category"
|
84
|
+
end
|
85
|
+
|
86
|
+
def letter_bars_node_name
|
87
|
+
"#{base_node_name}/aws:LetterBars/aws:Category"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Category
|
92
|
+
attr_accessor :path, :title, :sub_category_count, :total_listing_count
|
93
|
+
|
94
|
+
def initialize(hash)
|
95
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class LanguageCategory
|
100
|
+
attr_accessor :path, :title, :sub_category_count, :total_listing_count
|
101
|
+
|
102
|
+
def initialize(hash)
|
103
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class RelatedCategory
|
108
|
+
attr_accessor :path, :title, :sub_category_count, :total_listing_count
|
109
|
+
|
110
|
+
def initialize(hash)
|
111
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class LetterBar
|
116
|
+
attr_accessor :path, :title, :sub_category_count, :total_listing_count
|
117
|
+
|
118
|
+
def initialize(hash)
|
119
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Awis
|
2
|
+
module Models
|
3
|
+
class CategoryListings < Base
|
4
|
+
attr_accessor :count, :recursive_count, :listings
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@listings = []
|
8
|
+
setup_data!( loading_response(response) )
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_data!(response)
|
12
|
+
category_listings = []
|
13
|
+
|
14
|
+
response.each_node do |node, path|
|
15
|
+
text = node.inner_xml
|
16
|
+
text = text.to_i if text.to_i.to_s === text
|
17
|
+
|
18
|
+
if node.name == 'aws:RequestId'
|
19
|
+
@request_id ||= text
|
20
|
+
elsif node.name == 'aws:StatusCode'
|
21
|
+
@status_code ||= text
|
22
|
+
elsif node.name == 'aws:Count'
|
23
|
+
@count ||= text
|
24
|
+
elsif node.name == 'aws:RecursiveCount'
|
25
|
+
@recursive_count ||= text
|
26
|
+
elsif node.name == 'aws:DataUrl' && path == "#{base_node_name}/aws:DataUrl"
|
27
|
+
category_listings << { data_url: text }
|
28
|
+
elsif node.name == 'aws:Title' && path == "#{base_node_name}/aws:Title"
|
29
|
+
category_listings << { title: text }
|
30
|
+
elsif node.name == 'aws:PopularityRank' && path == "#{base_node_name}/aws:PopularityRank"
|
31
|
+
category_listings << { popularity_rank: text }
|
32
|
+
elsif node.name == 'aws:Description' && path == "#{base_node_name}/aws:Description"
|
33
|
+
category_listings << { description: text }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
relationship_collections(@listings, category_listings, 4, Listing)
|
38
|
+
end
|
39
|
+
|
40
|
+
def base_node_name
|
41
|
+
"#{root_node_name}/aws:CategoryListings/aws:Listings/aws:Listing"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Listing
|
46
|
+
attr_accessor :data_url, :title, :popularity_rank, :description
|
47
|
+
|
48
|
+
def initialize(hash)
|
49
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Awis
|
2
|
+
module Models
|
3
|
+
class SitesLinkingIn < Base
|
4
|
+
attr_accessor :sites
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
@sites = []
|
8
|
+
setup_data!( loading_response(response) )
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_data!(response)
|
12
|
+
sites = []
|
13
|
+
|
14
|
+
response.each_node do |node, path|
|
15
|
+
text = node.inner_xml
|
16
|
+
|
17
|
+
if node.name == 'aws:RequestId'
|
18
|
+
@request_id ||= text
|
19
|
+
elsif node.name == 'aws:StatusCode'
|
20
|
+
@status_code ||= text
|
21
|
+
elsif node.name == 'aws:Title'
|
22
|
+
sites << { title: text }
|
23
|
+
elsif node.name == 'aws:Url'
|
24
|
+
sites << { url: text }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
relationship_collections(@sites, sites, 2, Site)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Site
|
33
|
+
attr_accessor :title, :url
|
34
|
+
|
35
|
+
def initialize(hash)
|
36
|
+
hash.map { |k, v| instance_variable_set("@#{k}", v) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|