xing-backend 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06adc2c9335e9abd04144b3b77aa30b10ca66494
|
4
|
+
data.tar.gz: dab2b68d69fe03e1e510ccc4d3ae490f93065991
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db70e839274e8e2cd5f49583ec2435d6aef0fe25b10f3f7c4e5902922228d884a8dab7b11ed2cc7879718119234fc3e9ae60a546c29d51ed4e70a64baddc7559
|
7
|
+
data.tar.gz: 6e2e327f99f3fb411c0733d571d4cf5a3635c067b2eddf06eb40ada56643f7783947f34e66bc43ba912c041adf990df2f0d5ccc03bac0e8b13be1f5dd8b4df1a
|
data/app/models/role.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'xing/services/class_registry'
|
2
|
+
class Role
|
3
|
+
include Xing::Services::ClassRegistry
|
4
|
+
|
5
|
+
#def self.registrar; Role; end
|
6
|
+
|
7
|
+
def self.for(user)
|
8
|
+
registry[user.role_name].new.tap do |role|
|
9
|
+
role.user = user
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.users
|
14
|
+
User.where(:role_name => registrar.registry_key(self))
|
15
|
+
end
|
16
|
+
|
17
|
+
def role_name
|
18
|
+
user.role_name
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :user
|
22
|
+
|
23
|
+
Dir[File.join(Rails.root, 'app/models/role/*.rb')].each { |file| require file }
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
json:
|
2
|
+
errors:
|
3
|
+
# The default format to use in full error messages.
|
4
|
+
format: "%{attribute} %{message}"
|
5
|
+
|
6
|
+
# The values :model, :attribute and :value are always available for interpolation
|
7
|
+
# The value :count is available when applicable. Can be used for pluralization.
|
8
|
+
messages:
|
9
|
+
inclusion: "inclusion"
|
10
|
+
exclusion: "exclusion"
|
11
|
+
invalid: "invalid"
|
12
|
+
confirmation: "confirmation"
|
13
|
+
accepted: "accepted"
|
14
|
+
empty: "empty"
|
15
|
+
blank: "required"
|
16
|
+
present: "must_be_blank"
|
17
|
+
too_long: "longer_than_%{count}"
|
18
|
+
too_short: "shorter_than_%{count}"
|
19
|
+
wrong_length: "wrong_length_%{count}"
|
20
|
+
not_a_number: "not_a_number"
|
21
|
+
not_an_integer: "not_an_integer"
|
22
|
+
greater_than: "greater_than_%{count}"
|
23
|
+
greater_than_or_equal_to: "greater_than_%{count}"
|
24
|
+
equal_to: "equal_to_%{count}"
|
25
|
+
less_than: "less_than_%{count}"
|
26
|
+
less_than_or_equal_to: "less_than_or_equal_to_%{count}"
|
27
|
+
other_than: "other_than_%{count}"
|
28
|
+
odd: "odd"
|
29
|
+
even: "even"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
class Initial < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
|
4
|
+
# These are extensions that must be enabled in order to support this database
|
5
|
+
enable_extension "plpgsql"
|
6
|
+
enable_extension "hstore"
|
7
|
+
|
8
|
+
create_table "users", force: true do |t|
|
9
|
+
t.string "login", limit: 20, null: false
|
10
|
+
t.string "email"
|
11
|
+
t.string "first_name", limit: 60
|
12
|
+
t.string "last_name", limit: 60
|
13
|
+
t.integer "sign_in_count", default: 0, null: false
|
14
|
+
t.integer "failed_attempts", default: 0, null: false
|
15
|
+
t.datetime "last_request_at"
|
16
|
+
t.datetime "current_sign_in_at"
|
17
|
+
t.datetime "last_sign_in_at"
|
18
|
+
t.string "current_sign_in_ip"
|
19
|
+
t.string "last_sign_in_ip"
|
20
|
+
t.string "role_name"
|
21
|
+
t.datetime "created_at"
|
22
|
+
t.datetime "updated_at"
|
23
|
+
t.string "encrypted_password"
|
24
|
+
t.string "confirmation_token"
|
25
|
+
t.datetime "confirmed_at"
|
26
|
+
t.datetime "confirmation_sent_at"
|
27
|
+
t.string "reset_password_token"
|
28
|
+
t.datetime "reset_password_sent_at"
|
29
|
+
t.string "remember_token"
|
30
|
+
t.datetime "remember_created_at"
|
31
|
+
t.string "unlock_token"
|
32
|
+
t.datetime "locked_at"
|
33
|
+
end
|
34
|
+
|
35
|
+
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
|
36
|
+
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
|
37
|
+
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
|
38
|
+
add_index "users", ["unlock_token"], name: "index_users_on_unlock_token", unique: true, using: :btree
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.down
|
43
|
+
raise ActiveRecord::IrreversibleMigration
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class DeviseTokenAuthAddTokenInfoToUsers < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
change_table(:users) do |t|
|
4
|
+
## unique oauth id
|
5
|
+
t.string :provider
|
6
|
+
t.string :uid, :null => false, :default => ""
|
7
|
+
|
8
|
+
## Tokens
|
9
|
+
t.text :tokens
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :users, :uid, :unique => true
|
13
|
+
|
14
|
+
User.reset_column_information
|
15
|
+
User.all.each do |user|
|
16
|
+
user.uid = user.login
|
17
|
+
user.save
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/xing-backend.rb
CHANGED
data/lib/xing/engine.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'xing/nominal/yaml_config_validator'
|
2
|
+
|
3
|
+
module Xing
|
4
|
+
module Nominal
|
5
|
+
class DatabaseConfigValidator < YamlConfigValidator
|
6
|
+
DATABASE_CONFIG_FILE = 'config/database.yml'
|
7
|
+
COMMON_DATABASE_RULES = {
|
8
|
+
'adapter' => 'string',
|
9
|
+
'database' => 'string'
|
10
|
+
}
|
11
|
+
PROD_DATABASE_RULES = COMMON_DATABASE_RULES.merge({
|
12
|
+
'username' => 'string',
|
13
|
+
'password' => 'string',
|
14
|
+
'host' => 'string'
|
15
|
+
})
|
16
|
+
|
17
|
+
def rules(environment)
|
18
|
+
case environment
|
19
|
+
when 'production', 'staging'
|
20
|
+
PROD_DATABASE_RULES
|
21
|
+
else
|
22
|
+
COMMON_DATABASE_RULES
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def file_under_test
|
27
|
+
DATABASE_CONFIG_FILE
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Xing
|
2
|
+
module Nominal
|
3
|
+
module DependencyUtils
|
4
|
+
require 'pp'
|
5
|
+
def sh_or_fail(command, fail_message)
|
6
|
+
sh command do |ok, result|
|
7
|
+
dep_fail fail_message unless ok
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def dep_fail(fail_message, details = nil)
|
12
|
+
message = "Dependency Failed: " + fail_message
|
13
|
+
message += " (Details below):\n#{details.pretty_inspect}" if details
|
14
|
+
abort red(message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def red(string)
|
18
|
+
"\e[1;31m#{string}\e[0m"
|
19
|
+
end
|
20
|
+
|
21
|
+
def dep_success(message)
|
22
|
+
puts message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'xing/nominal/yaml_config_validator'
|
2
|
+
|
3
|
+
module Xing
|
4
|
+
module Nominal
|
5
|
+
class SecretsValidator < YamlConfigValidator
|
6
|
+
SECRETS_FILE = 'config/secrets.yml'
|
7
|
+
COMMON_SECRETS_VALIDATION = {
|
8
|
+
'secret_key_base' => 'string',
|
9
|
+
'smtp' => {
|
10
|
+
'address' => 'string',
|
11
|
+
'port' => 'integer',
|
12
|
+
'domain' => 'string',
|
13
|
+
'user_name' => 'string',
|
14
|
+
'password' => 'string'
|
15
|
+
},
|
16
|
+
'email' => {
|
17
|
+
'from' => 'email',
|
18
|
+
'reply_to' => 'email',
|
19
|
+
'from_domain' => 'string'
|
20
|
+
},
|
21
|
+
'snapshot_server' => {
|
22
|
+
'url' => 'string',
|
23
|
+
'user' => 'string',
|
24
|
+
'password' => 'string'
|
25
|
+
},
|
26
|
+
'sitemap_base_url' => 'string'
|
27
|
+
}
|
28
|
+
|
29
|
+
# Development needs the additional email key 'test'
|
30
|
+
DEV_SECRETS_VALIDATION = COMMON_SECRETS_VALIDATION.deep_merge(
|
31
|
+
'email' => {
|
32
|
+
'test' => 'email'
|
33
|
+
}
|
34
|
+
)
|
35
|
+
|
36
|
+
def rules(environment)
|
37
|
+
case environment
|
38
|
+
when 'development'
|
39
|
+
DEV_SECRETS_VALIDATION
|
40
|
+
else
|
41
|
+
COMMON_SECRETS_VALIDATION
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def file_under_test
|
46
|
+
SECRETS_FILE
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'hash_validator'
|
2
|
+
require 'xing/nominal/dependency_utils'
|
3
|
+
|
4
|
+
module Xing
|
5
|
+
module Nominal
|
6
|
+
class YamlConfigValidator
|
7
|
+
include DependencyUtils
|
8
|
+
|
9
|
+
attr_accessor :yaml_hash, :results
|
10
|
+
def initialize
|
11
|
+
self.yaml_hash = YAML.load(File.open(file_under_test))
|
12
|
+
self.results = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Validate only with common secrets requirements
|
16
|
+
def validate(*envs)
|
17
|
+
envs << 'test' if envs.include?('development')
|
18
|
+
self.results.push(*envs.map{ |env|
|
19
|
+
HashValidator.validate(yaml_hash, { env => rules(env) })
|
20
|
+
})
|
21
|
+
end
|
22
|
+
|
23
|
+
def report!
|
24
|
+
if errors.blank?
|
25
|
+
dep_success("#{file_under_test} appears correctly formatted.")
|
26
|
+
else
|
27
|
+
dep_fail("#{file_under_test} didn't contain required values.", errors)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def errors
|
32
|
+
self.results.reduce({}) do |errs, validator|
|
33
|
+
errs.deep_merge!(validator.errors)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_existence
|
38
|
+
unless File.exists?(file_under_test)
|
39
|
+
dep_fail("Please create #{file_under_test}, check the .example for format")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/xing/services.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Xing
|
2
|
+
module Services
|
3
|
+
module ClassRegistry
|
4
|
+
module ClassMethods
|
5
|
+
def registry
|
6
|
+
@registry ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(name, klass=self)
|
10
|
+
raise "Invalid registration: #{name} exists" if registrar.registry.has_key?(name) && registrar.registry[name] != klass
|
11
|
+
registrar.registry[name] = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def registry_key(klass)
|
15
|
+
registrar.registry.select{ |key, val| val == klass}.keys.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def registry_get(name)
|
19
|
+
registrar.registry.fetch(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.included(base)
|
24
|
+
base.define_singleton_method(:registrar) do
|
25
|
+
base
|
26
|
+
end
|
27
|
+
base.extend(ClassMethods)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Xing::Services
|
2
2
|
# If you want to use the PagedList serializers, but are using resources that
|
3
3
|
# aren't actually provided by Kaminari, you can instead feed them to
|
4
|
-
#
|
5
|
-
class
|
4
|
+
# PagedWrapper and you should get everything you need
|
5
|
+
class PagedWrapper
|
6
6
|
include Enumerable
|
7
7
|
|
8
8
|
def initialize(list, page_num, total_items, per_page)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Xing
|
2
|
+
module Snapshot
|
3
|
+
module DomainHelpers
|
4
|
+
def domain(url = nil)
|
5
|
+
if url
|
6
|
+
url
|
7
|
+
elsif defined? Rails.application.secrets.sitemap_base_url
|
8
|
+
Rails.application.secrets.sitemap_base_url
|
9
|
+
else
|
10
|
+
raise "No Domain is set for the sitemap. Please set it in secrets.yml."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def page_frontend_url(path)
|
15
|
+
if PAGES_FRONTEND_URL.present?
|
16
|
+
PAGES_FRONTEND_URL + "/" + path
|
17
|
+
else
|
18
|
+
path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'addressable/uri'
|
3
|
+
require 'xing/snapshot/writer'
|
4
|
+
require 'sidekiq/worker'
|
5
|
+
|
6
|
+
module Xing
|
7
|
+
module Snapshot
|
8
|
+
class Fetcher
|
9
|
+
include Sidekiq::Worker
|
10
|
+
include Writer
|
11
|
+
|
12
|
+
def perform(url, path)
|
13
|
+
admin_server = Rails.application.secrets.snapshot_server['url']
|
14
|
+
user_password = "#{Rails.application.secrets.snapshot_server['user']}:#{Rails.application.secrets.snapshot_server['password']}"
|
15
|
+
snapshot_url = Addressable::URI.join(url,path).to_s
|
16
|
+
request = Typhoeus::Request.new(admin_server, userpwd: user_password, params: { url: snapshot_url })
|
17
|
+
|
18
|
+
hydra = Typhoeus::Hydra.new
|
19
|
+
hydra.queue(request)
|
20
|
+
hydra.run
|
21
|
+
|
22
|
+
response = request.response
|
23
|
+
|
24
|
+
if response.success?
|
25
|
+
html = response.body
|
26
|
+
write(path, html)
|
27
|
+
else
|
28
|
+
logger.warn response.status_message
|
29
|
+
logger.warn response.body
|
30
|
+
raise "Query to #{admin_server} for #{path} failed!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|