awis-sdk-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -0,0 +1,15 @@
1
+ require "singleton"
2
+
3
+ module Awis
4
+ class Config
5
+ include Singleton
6
+ attr_accessor :access_key_id, :secret_access_key, :proxy, :debug
7
+ end
8
+
9
+ def self.config
10
+ if block_given?
11
+ yield Config.instance
12
+ end
13
+ Config.instance
14
+ end
15
+ end
@@ -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
@@ -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