xing-backend 0.0.19 → 0.0.20
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.
- checksums.yaml +4 -4
- data/app/models/role.rb +24 -0
- data/config/locales/json.yml +29 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20140828011806_initial.rb +45 -0
- data/db/migrate/20140914030703_devise_token_auth_add_token_info_to_users.rb +20 -0
- data/db/migrate/20140929192921_remove_login_from_users.rb +5 -0
- data/lib/xing-backend.rb +1 -0
- data/lib/xing/engine.rb +4 -0
- data/lib/xing/nominal/database_config_validator.rb +31 -0
- data/lib/xing/nominal/dependency_utils.rb +26 -0
- data/lib/xing/nominal/secrets_validator.rb +50 -0
- data/lib/xing/nominal/yaml_config_validator.rb +44 -0
- data/lib/xing/services.rb +0 -2
- data/lib/xing/services/class_registry.rb +31 -0
- data/lib/xing/services/{page_wrapper.rb → paged_wrapper.rb} +2 -2
- data/lib/xing/snapshot.rb +4 -0
- data/lib/xing/snapshot/domain_helpers.rb +24 -0
- data/lib/xing/snapshot/fetcher.rb +35 -0
- data/lib/xing/snapshot/local_site_snapshot.rb +37 -0
- data/lib/xing/snapshot/remote_site_snapshot.rb +16 -0
- data/lib/xing/snapshot/site_page_set.rb +29 -0
- data/lib/xing/snapshot/site_snapshot.rb +41 -0
- data/lib/xing/snapshot/sitemap.rb +68 -0
- data/lib/xing/snapshot/writer.rb +15 -0
- data/lib/xing/spec_helpers.rb +7 -0
- data/lib/xing/spec_helpers/api_response_matchers.rb +26 -0
- data/lib/xing/spec_helpers/ci_support.rb +6 -0
- data/lib/xing/spec_helpers/dom_equiv.rb +26 -0
- data/lib/xing/spec_helpers/json_requests.rb +58 -0
- data/lib/xing/spec_helpers/routing_spec_patch.rb +28 -0
- data/lib/xing/spec_helpers/split_servers.rb +15 -0
- data/lib/xing/spec_helpers/test_url_helpers.rb +5 -0
- data/lib/xing/static.rb +1 -0
- data/lib/xing/static/backend_url_cookie.rb +16 -0
- data/lib/xing/static/goto_param.rb +30 -0
- data/lib/xing/static/logger.rb +11 -0
- data/lib/xing/static/rack_app.rb +40 -0
- data/lib/xing/tasks/all.rake +4 -0
- data/lib/xing/tasks/db_recycle.rake +4 -0
- data/lib/xing/tasks/dependencies_common.rake +49 -0
- data/lib/xing/tasks/sample_data.rake +49 -0
- data/lib/xing/tasks/take_snapshot.rake +4 -0
- data/spec/xing/builders/list_builder_spec.rb +0 -2
- data/spec/xing/builders/ordered_list_builder_spec.rb +0 -2
- data/spec/xing/nominal/database_config_validator_spec.rb +98 -0
- data/spec/xing/nominal/secrets_validator_spec.rb +78 -0
- data/spec/xing/serializers/list_spec.rb +116 -0
- data/spec/xing/serializers/paged_index_spec.rb +2 -2
- data/spec/xing/serializers/paged_list_spec.rb +1 -1
- data/spec/xing/services/error_converter_spec.rb +1 -3
- data/spec/xing/services/paged_wrapper_spec.rb +30 -0
- data/spec/xing/snapshot/remote_snapshot_fetcher_spec.rb +81 -0
- data/spec/xing/{services → snapshot}/snapshot_fetcher_spec.rb +6 -4
- data/spec_help/dummy/db/test.sqlite3 +0 -0
- data/spec_help/dummy/log/test.log +143 -0
- data/spec_help/file-sandbox.rb +164 -0
- data/spec_help/spec_helper.rb +0 -2
- metadata +152 -12
- data/lib/xing/services/snapshot_fetcher.rb +0 -33
- data/lib/xing/services/snapshot_writer.rb +0 -19
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'selenium-webdriver'
|
2
|
+
require 'xing/snapshot/site_snapshot'
|
3
|
+
require 'xing/snapshot/writer'
|
4
|
+
|
5
|
+
module Xing
|
6
|
+
module Snapshot
|
7
|
+
class LocalSiteSnapshot < SiteSnapshot
|
8
|
+
include Writer
|
9
|
+
|
10
|
+
def initialize(url)
|
11
|
+
super(url)
|
12
|
+
@wait = Selenium::WebDriver::Wait.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup
|
16
|
+
@driver = Selenium::WebDriver.for :chrome
|
17
|
+
end
|
18
|
+
attr_accessor :driver, :wait
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
@driver.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch(url, path)
|
25
|
+
@driver.navigate.to(url+path)
|
26
|
+
# better way to wait till javascript complete?
|
27
|
+
@wait.until do
|
28
|
+
@driver.execute_script("return window.frontendContentLoaded == true;")
|
29
|
+
end
|
30
|
+
element = @driver.find_element(:tag_name, "html")
|
31
|
+
html = element.attribute("outerHTML")
|
32
|
+
write(path, html)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'xing/snapshot/site_snapshot'
|
2
|
+
require 'xing/snapshot/fetcher'
|
3
|
+
|
4
|
+
module Xing
|
5
|
+
module Snapshot
|
6
|
+
class RemoteSiteSnapshot < SiteSnapshot
|
7
|
+
def setup
|
8
|
+
@fetcher = Xing::Snapshot::Fetcher.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(url, path)
|
12
|
+
@fetcher.perform(url, path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'xing/snapshot/domain_helpers'
|
2
|
+
|
3
|
+
module Xing
|
4
|
+
module Snapshot
|
5
|
+
class SitePageSet
|
6
|
+
include DomainHelpers
|
7
|
+
|
8
|
+
def initialize(url)
|
9
|
+
@url = domain(url)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pages_to_visit
|
13
|
+
@pages_to_visit ||= Page.published.where.not(type: "Page::Homepage")
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_pages(&block)
|
17
|
+
STATIC_PATHS_FOR_SITEMAP.each do |path|
|
18
|
+
yield(@url,path,Time.now)
|
19
|
+
end
|
20
|
+
|
21
|
+
pages_to_visit.each do |page|
|
22
|
+
path = page_frontend_url(page.url_slug)
|
23
|
+
yield(@url, path, page.updated_at)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "xing/snapshot/site_page_set"
|
2
|
+
|
3
|
+
module Xing
|
4
|
+
module Snapshot
|
5
|
+
class SiteSnapshot
|
6
|
+
class << self
|
7
|
+
def create!(url)
|
8
|
+
self.new(url).create!
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(url)
|
13
|
+
@url = url
|
14
|
+
end
|
15
|
+
attr_accessor :url
|
16
|
+
|
17
|
+
def create!
|
18
|
+
@sitemap_page_set = SitePageSet.new(url)
|
19
|
+
setup
|
20
|
+
generate_snapshot
|
21
|
+
teardown
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_snapshot
|
28
|
+
@sitemap_page_set.visit_pages do |url, path, updated_at|
|
29
|
+
fetch(url, path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch(url, path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'xing/snapshot/site_page_set'
|
3
|
+
require 'xing/snapshot/domain_helpers'
|
4
|
+
|
5
|
+
module Xing
|
6
|
+
module Snapshot
|
7
|
+
class Sitemap
|
8
|
+
class << self
|
9
|
+
include DomainHelpers
|
10
|
+
|
11
|
+
def create!(url = nil)
|
12
|
+
@bad_pages = []
|
13
|
+
|
14
|
+
@sitemap_page_set = SitePageSet.new(url)
|
15
|
+
|
16
|
+
generate_sitemap
|
17
|
+
update_search_engines if Rails.env.production?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def generate_sitemap
|
22
|
+
xml_str = ""
|
23
|
+
|
24
|
+
xml = Builder::XmlMarkup.new(:target => xml_str, :indent => 2)
|
25
|
+
|
26
|
+
xml.instruct!
|
27
|
+
xml.urlset(:xmlns=>'http://www.sitemaps.org/schemas/sitemap/0.9') {
|
28
|
+
@sitemap_page_set.visit_pages do |url, path, update_time|
|
29
|
+
xml.url {
|
30
|
+
xml.loc(url+path)
|
31
|
+
xml.lastmod(update_time.utc.strftime('%Y-%m-%dT%H:%M:%S+00:00'))
|
32
|
+
}
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
save_file(xml_str)
|
37
|
+
end
|
38
|
+
|
39
|
+
def save_file(xml)
|
40
|
+
File.open("#{ Rails.root }/public/sitemap.xml", "w+") do |f|
|
41
|
+
f.write(xml)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_search_engine(host, path, sitemap_uri)
|
46
|
+
Rails.logger.info{"Notifying #{host}"}
|
47
|
+
begin
|
48
|
+
res = Net::HTTP.get_response(host, path + sitemap_uri)
|
49
|
+
rescue Object => ex
|
50
|
+
Rails.logger.info "Error while notifying #{host}: #{ex.inspect}"
|
51
|
+
end
|
52
|
+
Rails.logger.info res.class
|
53
|
+
end
|
54
|
+
|
55
|
+
# Notify popular search engines of the updated sitemap.xml
|
56
|
+
def update_search_engines
|
57
|
+
sitemap_uri = domain + 'sitemap.xml'
|
58
|
+
escaped_sitemap_uri = CGI.escape(sitemap_uri)
|
59
|
+
|
60
|
+
update_search_engine('www.google.com', '/webmasters/tools/ping?sitemap=', escaped_sitemap_uri)
|
61
|
+
update_search_engine('search.yahooapis.com', '/SiteExplorerService/V1/updateNotification?appid=SitemapWriter&url=', escaped_sitemap_uri)
|
62
|
+
update_search_engine('www.bing.com', '/webmaster/ping.aspx?siteMap=', escaped_sitemap_uri)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Xing
|
2
|
+
module Snapshot
|
3
|
+
module Writer
|
4
|
+
def write(path, html)
|
5
|
+
snapshot_file = "#{ Rails.root }/public/frontend_snapshots/#{path.present? ? path : 'index'}.html"
|
6
|
+
dirname = File.dirname(snapshot_file)
|
7
|
+
FileUtils.mkdir_p(dirname)
|
8
|
+
|
9
|
+
File.open(snapshot_file, "w+:ASCII-8BIT:UTF-8") do |f|
|
10
|
+
f.write(html)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'xing/spec_helpers/api_response_matchers'
|
2
|
+
require 'xing/spec_helpers/ci_support'
|
3
|
+
require 'xing/spec_helpers/dom_equiv'
|
4
|
+
require 'xing/spec_helpers/json_requests'
|
5
|
+
require 'xing/spec_helpers/routing_spec_patch'
|
6
|
+
require 'xing/spec_helpers/split_servers'
|
7
|
+
require 'xing/spec_helpers/test_url_helpers'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module APIResponseMatchers
|
2
|
+
extend RSpec::Matchers::DSL
|
3
|
+
|
4
|
+
matcher :reject_as_unprocessable do
|
5
|
+
match_unless_raises ActiveSupport::TestCase::Assertion do |response|
|
6
|
+
expect(response.status).to eq(422)
|
7
|
+
end
|
8
|
+
|
9
|
+
failure_message do |response|
|
10
|
+
"Expected response to be a 422: Unprocessable Entity"
|
11
|
+
end
|
12
|
+
|
13
|
+
failure_message_when_negated do |source|
|
14
|
+
"Expected response not to be a 422: Unprocessable Entity"
|
15
|
+
end
|
16
|
+
|
17
|
+
description do
|
18
|
+
"should send a 422: Unprocessable Entity status"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class RSpec::Core::ExampleGroup
|
25
|
+
include APIResponseMatchers
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DomEquivalence
|
2
|
+
extend RSpec::Matchers::DSL
|
3
|
+
|
4
|
+
matcher :match_dom_of do |target|
|
5
|
+
match_unless_raises ActiveSupport::TestCase::Assertion do |source|
|
6
|
+
assert_dom_equal source, target
|
7
|
+
end
|
8
|
+
|
9
|
+
failure_message do |source|
|
10
|
+
"Expected #{source} to have equivalent DOM to #{target}"
|
11
|
+
end
|
12
|
+
|
13
|
+
failure_message_when_negated do |source|
|
14
|
+
"Expected #{source} not to have equivalent DOM to #{target}"
|
15
|
+
end
|
16
|
+
|
17
|
+
description do
|
18
|
+
"should be DOM equivalent to #{target}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class RSpec::Core::ExampleGroup
|
25
|
+
include DomEquivalence
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module JSONRequests
|
2
|
+
|
3
|
+
def json_get(url, arg2 = nil)
|
4
|
+
get rootify(url), arg2, { 'HTTP_ACCEPT' => 'application/json' }
|
5
|
+
end
|
6
|
+
|
7
|
+
def json_post(url, arg2 = nil)
|
8
|
+
post rootify(url), arg2, { 'HTTP_ACCEPT' => 'application/json' }
|
9
|
+
end
|
10
|
+
|
11
|
+
def json_put(url, arg2 = nil)
|
12
|
+
put rootify(url), arg2, { 'HTTP_ACCEPT' => 'application/json' }
|
13
|
+
end
|
14
|
+
|
15
|
+
def json_delete(url, arg2 = nil)
|
16
|
+
delete rootify(url), arg2, { 'HTTP_ACCEPT' => 'application/json' }
|
17
|
+
end
|
18
|
+
|
19
|
+
def authenticated_json_get(user, url, arg2 = nil)
|
20
|
+
auth_header = user.create_new_auth_token
|
21
|
+
get rootify(url), arg2, auth_header.merge({ 'HTTP_ACCEPT' => 'application/json' })
|
22
|
+
end
|
23
|
+
|
24
|
+
def authenticated_json_post(user, url, arg2 = nil)
|
25
|
+
auth_header = user.create_new_auth_token
|
26
|
+
post rootify(url), arg2, auth_header.merge({ 'HTTP_ACCEPT' => 'application/json' })
|
27
|
+
end
|
28
|
+
|
29
|
+
def authenticated_json_put(user, url, arg2 = nil)
|
30
|
+
auth_header = user.create_new_auth_token
|
31
|
+
put rootify(url), arg2, auth_header.merge({ 'HTTP_ACCEPT' => 'application/json' })
|
32
|
+
end
|
33
|
+
|
34
|
+
def authenticated_json_delete(user, url, arg2 = nil)
|
35
|
+
auth_header = user.create_new_auth_token
|
36
|
+
delete rootify(url), arg2, auth_header.merge({ 'HTTP_ACCEPT' => 'application/json' })
|
37
|
+
end
|
38
|
+
|
39
|
+
def rootify(url)
|
40
|
+
if url[0] == '/'
|
41
|
+
url
|
42
|
+
else
|
43
|
+
"/#{url}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
module RSpec::Rails::RequestExampleGroup
|
51
|
+
include JSONRequests
|
52
|
+
include JsonSpec::Matchers
|
53
|
+
include JsonSpec::Helpers
|
54
|
+
end
|
55
|
+
|
56
|
+
RSpec.configure do |config|
|
57
|
+
config.include JsonSpec::Helpers, :type => :serializer
|
58
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
# Matchers to help with specs for routing code.
|
5
|
+
module RoutingMatchers
|
6
|
+
class RouteToMatcher
|
7
|
+
def matches_with_patches?(verb_to_path_map)
|
8
|
+
path = verb_to_path_map.values.first
|
9
|
+
verb_to_path_map[verb_to_path_map.keys.first] = "http://#{BACKEND_SUBDOMAIN}.example.com#{path[0] == "/" ? "" : "/"}#{path}";
|
10
|
+
matches_without_patches?(verb_to_path_map)
|
11
|
+
end
|
12
|
+
alias_method_chain :matches?, :patches
|
13
|
+
end
|
14
|
+
|
15
|
+
class FrontendRouteToMatcher < RouteToMatcher
|
16
|
+
def matches?(verb_to_path_map)
|
17
|
+
matches_without_patches?(verb_to_path_map)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def frontend_route_to(*expected)
|
22
|
+
FrontendRouteToMatcher.new(self, *expected)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
require 'static-app'
|
3
|
+
|
4
|
+
Capybara.configure do |capy|
|
5
|
+
backend_server = Capybara::Server.new(Capybara.app)
|
6
|
+
backend_server.boot
|
7
|
+
puts "Rails API server started on #{backend_server.host}:#{backend_server.port}"
|
8
|
+
ActionMailer::Base.default_url_options[:host] = "#{backend_server.host}:#{backend_server.port}"
|
9
|
+
Capybara.app = APP_MODULE::StaticApp.build("../frontend/bin", backend_server.port)
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.waterpig_clearable_logs << 'test_static'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
rescue LoadError
|
15
|
+
end
|
data/lib/xing/static.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'xing/static/rack_app'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Xing
|
2
|
+
module Static
|
3
|
+
class BackendUrlCookie
|
4
|
+
def initialize(app, backend_url)
|
5
|
+
@backend_url = backend_url
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
status, headers, body = @app.call(env)
|
11
|
+
headers["Set-Cookie"] = [(headers["Set-Cookie"]), "lrdBackendUrl=#@backend_url"].compact.join(";") unless @backend_url.nil?
|
12
|
+
[ status, headers, body ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'pp'
|
2
|
+
module Xing
|
3
|
+
module Static
|
4
|
+
class GotoParam
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
status, headers, body = @app.call(env)
|
11
|
+
default = [ status, headers, body ]
|
12
|
+
request_path = env["SCRIPT_NAME"] + env["PATH_INFO"]
|
13
|
+
if env["QUERY_STRING"]
|
14
|
+
request_path += "&#{env["QUERY_STRING"]}"
|
15
|
+
end
|
16
|
+
|
17
|
+
redirect = [ 301, headers.merge("Location" => "/?goto=#{request_path}", "Content-Length" => "0"), [] ]
|
18
|
+
|
19
|
+
return default unless status == 404
|
20
|
+
return default if /\A(assets|fonts|system)/ =~ request_path
|
21
|
+
return default if /\.(xml|html|ico|txt)\z/ =~ request_path
|
22
|
+
return default if /goto=/ =~ env["QUERY_STRING"]
|
23
|
+
|
24
|
+
return redirect
|
25
|
+
rescue => ex
|
26
|
+
pp ex
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|