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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/role.rb +24 -0
  3. data/config/locales/json.yml +29 -0
  4. data/config/routes.rb +3 -0
  5. data/db/migrate/20140828011806_initial.rb +45 -0
  6. data/db/migrate/20140914030703_devise_token_auth_add_token_info_to_users.rb +20 -0
  7. data/db/migrate/20140929192921_remove_login_from_users.rb +5 -0
  8. data/lib/xing-backend.rb +1 -0
  9. data/lib/xing/engine.rb +4 -0
  10. data/lib/xing/nominal/database_config_validator.rb +31 -0
  11. data/lib/xing/nominal/dependency_utils.rb +26 -0
  12. data/lib/xing/nominal/secrets_validator.rb +50 -0
  13. data/lib/xing/nominal/yaml_config_validator.rb +44 -0
  14. data/lib/xing/services.rb +0 -2
  15. data/lib/xing/services/class_registry.rb +31 -0
  16. data/lib/xing/services/{page_wrapper.rb → paged_wrapper.rb} +2 -2
  17. data/lib/xing/snapshot.rb +4 -0
  18. data/lib/xing/snapshot/domain_helpers.rb +24 -0
  19. data/lib/xing/snapshot/fetcher.rb +35 -0
  20. data/lib/xing/snapshot/local_site_snapshot.rb +37 -0
  21. data/lib/xing/snapshot/remote_site_snapshot.rb +16 -0
  22. data/lib/xing/snapshot/site_page_set.rb +29 -0
  23. data/lib/xing/snapshot/site_snapshot.rb +41 -0
  24. data/lib/xing/snapshot/sitemap.rb +68 -0
  25. data/lib/xing/snapshot/writer.rb +15 -0
  26. data/lib/xing/spec_helpers.rb +7 -0
  27. data/lib/xing/spec_helpers/api_response_matchers.rb +26 -0
  28. data/lib/xing/spec_helpers/ci_support.rb +6 -0
  29. data/lib/xing/spec_helpers/dom_equiv.rb +26 -0
  30. data/lib/xing/spec_helpers/json_requests.rb +58 -0
  31. data/lib/xing/spec_helpers/routing_spec_patch.rb +28 -0
  32. data/lib/xing/spec_helpers/split_servers.rb +15 -0
  33. data/lib/xing/spec_helpers/test_url_helpers.rb +5 -0
  34. data/lib/xing/static.rb +1 -0
  35. data/lib/xing/static/backend_url_cookie.rb +16 -0
  36. data/lib/xing/static/goto_param.rb +30 -0
  37. data/lib/xing/static/logger.rb +11 -0
  38. data/lib/xing/static/rack_app.rb +40 -0
  39. data/lib/xing/tasks/all.rake +4 -0
  40. data/lib/xing/tasks/db_recycle.rake +4 -0
  41. data/lib/xing/tasks/dependencies_common.rake +49 -0
  42. data/lib/xing/tasks/sample_data.rake +49 -0
  43. data/lib/xing/tasks/take_snapshot.rake +4 -0
  44. data/spec/xing/builders/list_builder_spec.rb +0 -2
  45. data/spec/xing/builders/ordered_list_builder_spec.rb +0 -2
  46. data/spec/xing/nominal/database_config_validator_spec.rb +98 -0
  47. data/spec/xing/nominal/secrets_validator_spec.rb +78 -0
  48. data/spec/xing/serializers/list_spec.rb +116 -0
  49. data/spec/xing/serializers/paged_index_spec.rb +2 -2
  50. data/spec/xing/serializers/paged_list_spec.rb +1 -1
  51. data/spec/xing/services/error_converter_spec.rb +1 -3
  52. data/spec/xing/services/paged_wrapper_spec.rb +30 -0
  53. data/spec/xing/snapshot/remote_snapshot_fetcher_spec.rb +81 -0
  54. data/spec/xing/{services → snapshot}/snapshot_fetcher_spec.rb +6 -4
  55. data/spec_help/dummy/db/test.sqlite3 +0 -0
  56. data/spec_help/dummy/log/test.log +143 -0
  57. data/spec_help/file-sandbox.rb +164 -0
  58. data/spec_help/spec_helper.rb +0 -2
  59. metadata +152 -12
  60. data/lib/xing/services/snapshot_fetcher.rb +0 -33
  61. 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,6 @@
1
+ $RAW_SELENIUM_WAIT = 5
2
+ if ENV["CI_SERVER"] == "yes"
3
+ puts "Setting up longer timeouts for CI"
4
+ Capybara.default_wait_time = 30
5
+ $RAW_SELENIUM_WAIT = 30
6
+ 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
@@ -0,0 +1,5 @@
1
+ module TestUrlHelpers
2
+ def full_url(rails_path)
3
+ return "http://127.0.0.1:"+Capybara.current_session.server.port.to_s+rails_path
4
+ end
5
+ end
@@ -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