qa_server 0.1.99
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +71 -0
- data/Gemfile +55 -0
- data/Gemfile.lock +411 -0
- data/LICENSE +201 -0
- data/README.md +187 -0
- data/Rakefile +18 -0
- data/app/assets/images/qa_server/LD4L-bg.png +0 -0
- data/app/assets/images/qa_server/cornell-reduced-white.svg +160 -0
- data/app/assets/images/qa_server/iowa-logo.png +0 -0
- data/app/assets/images/qa_server/samvera-fall-font1-1000w-light.png +0 -0
- data/app/assets/javascripts/qa_server.js +0 -0
- data/app/assets/stylesheets/qa_server/_authorities.scss +0 -0
- data/app/assets/stylesheets/qa_server/_check-status.scss +72 -0
- data/app/assets/stylesheets/qa_server/_footer.scss +20 -0
- data/app/assets/stylesheets/qa_server/_header.scss +51 -0
- data/app/assets/stylesheets/qa_server/_home-page.scss +15 -0
- data/app/assets/stylesheets/qa_server/_monitor-status.scss +13 -0
- data/app/assets/stylesheets/qa_server/_qa_server.scss +36 -0
- data/app/assets/stylesheets/qa_server/_styles.scss +27 -0
- data/app/assets/stylesheets/qa_server/_usage.scss +0 -0
- data/app/controllers/qa_server/authority_list_controller.rb +14 -0
- data/app/controllers/qa_server/authority_validation_controller.rb +77 -0
- data/app/controllers/qa_server/check_status_controller.rb +38 -0
- data/app/controllers/qa_server/homepage_controller.rb +8 -0
- data/app/controllers/qa_server/monitor_status_controller.rb +79 -0
- data/app/controllers/qa_server/usage_controller.rb +8 -0
- data/app/loggers/qa_server/scenario_logger.rb +84 -0
- data/app/models/qa_server/authority_scenario.rb +35 -0
- data/app/models/qa_server/authority_status.rb +18 -0
- data/app/models/qa_server/authority_status_failure.rb +7 -0
- data/app/models/qa_server/scenarios.rb +71 -0
- data/app/models/qa_server/search_scenario.rb +58 -0
- data/app/models/qa_server/term_scenario.rb +50 -0
- data/app/presenters/qa_server/authority_list_presenter.rb +22 -0
- data/app/presenters/qa_server/check_status_presenter.rb +96 -0
- data/app/presenters/qa_server/monitor_status_presenter.rb +108 -0
- data/app/services/qa_server/authority_lister_service.rb +31 -0
- data/app/services/qa_server/authority_loader_service.rb +38 -0
- data/app/services/qa_server/authority_validator_service.rb +41 -0
- data/app/services/qa_server/database_migrator.rb +70 -0
- data/app/services/qa_server/scenarios_loader_service.rb +57 -0
- data/app/validators/qa_server/scenario_validator.rb +134 -0
- data/app/validators/qa_server/search_scenario_validator.rb +84 -0
- data/app/validators/qa_server/term_scenario_validator.rb +42 -0
- data/app/views/layouts/qa_server.html.erb +65 -0
- data/app/views/qa_server/authority_list/index.html.erb +27 -0
- data/app/views/qa_server/check_status/index.html.erb +104 -0
- data/app/views/qa_server/homepage/index.html.erb +10 -0
- data/app/views/qa_server/monitor_status/index.html.erb +78 -0
- data/app/views/qa_server/usage/index.html.erb +106 -0
- data/app/views/shared/_footer.html.erb +24 -0
- data/config/locales/qa_server.en.yml +68 -0
- data/config/routes.rb +12 -0
- data/lib/generators/qa_server/assets_generator.rb +35 -0
- data/lib/generators/qa_server/config_generator.rb +31 -0
- data/lib/generators/qa_server/install_generator.rb +60 -0
- data/lib/generators/qa_server/models_generator.rb +18 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/agrovoc_direct.json +69 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/agrovoc_ld4l_cache.json +85 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/dbpedia_direct.json +36 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/dbpedia_ld4l_cache.json +83 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/geonames_direct.json +68 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/geonames_ld4l_cache.json +102 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_aat_ld4l_cache.json +109 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_tgn_ld4l_cache.json +72 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_ulan_ld4l_cache.json +81 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/loc_direct.json +47 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/locgenres_ld4l_cache.json +99 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/locnames_ld4l_cache.json +89 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/locsubjects_ld4l_cache.json +82 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/mesh_ld4l_cache.json +70 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/nalt_direct.json +32 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/nalt_ld4l_cache.json +84 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/oclcfast_direct.json +81 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/oclcfast_ld4l_cache.json +86 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/agrovoc_direct_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/agrovoc_ld4l_cache_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/agrovoc_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/dbpedia_direct_validation.yml +6 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/dbpedia_ld4l_cache_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/geonames_direct_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/geonames_ld4l_cache_validation.yml +41 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/getty_aat_ld4l_cache_validation.yml +115 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/getty_tgn_ld4l_cache_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/getty_ulan_ld4l_cache_validation.yml +15 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/loc_direct_validation.yml +25 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/loc_validation.yml +22 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locgenres_ld4l_cache_validation.yml +99 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locnames_ld4l_cache_validation.yml +27 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locsubjects_ld4l_cache_validation.yml +27 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/mesh_ld4l_cache_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/nalt_direct_validation.yml +6 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/nalt_ld4l_cache_validation.yml +9 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/oclc_fast_validation.yml +58 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/oclcfast_direct_validation.yml +58 -0
- data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/oclcfast_ld4l_cache_validation.yml +30 -0
- data/lib/generators/qa_server/templates/config/locales/qa_server.en.yml +9 -0
- data/lib/generators/qa_server/templates/db/migrate/20180313045551_create_cron_authority_status.rb.erb +9 -0
- data/lib/generators/qa_server/templates/db/migrate/20180508045549_rename_cron_authority_status_to_authority_status.rb.erb +5 -0
- data/lib/generators/qa_server/templates/db/migrate/20180508045551_create_authority_status_failure.rb.erb +15 -0
- data/lib/generators/qa_server/templates/qa_server.scss +5 -0
- data/lib/qa_server/engine.rb +19 -0
- data/lib/qa_server/version.rb +3 -0
- data/lib/qa_server.rb +6 -0
- data/lib/tasks/install.rake +8 -0
- data/lib/tasks/qa_server_tasks.rake +4 -0
- data/qa_server.gemspec +39 -0
- data/spec/.gitignore +1 -0
- data/spec/rails_helper.rb +2 -0
- data/spec/spec_helper.rb +283 -0
- data/spec/test_app_templates/Gemfile.extra +5 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +15 -0
- data/tasks/qa_server_dev.rake +16 -0
- metadata +275 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
# Holds all scenarios for an authority.
|
2
|
+
module QaServer
|
3
|
+
class Scenarios
|
4
|
+
|
5
|
+
AUTHORITY_SCENARIO = 'authority'.freeze
|
6
|
+
TERM_SCENARIOS = 'term'.freeze
|
7
|
+
SEARCH_SCENARIOS = 'search'.freeze
|
8
|
+
|
9
|
+
# @return [Qa::Authorities::LinkedData::GenericAuthority] authority instance the scenarios run against
|
10
|
+
attr_reader :authority
|
11
|
+
|
12
|
+
# @return [String] name of the authority the scenarios run against (e.g. 'agrovoc_direct')
|
13
|
+
attr_reader :authority_name
|
14
|
+
|
15
|
+
# @return [Array<TermScenario>] the term scenarios to run against the authority
|
16
|
+
attr_reader :term_scenarios
|
17
|
+
|
18
|
+
# @return [Array<SearchScenario>] the search scenarios to run against the authority
|
19
|
+
attr_reader :search_scenarios
|
20
|
+
|
21
|
+
# @param authority [Qa::Authorities::LinkedData::GenericAuthority] the instance of the QA authority
|
22
|
+
# @param authoity_name [String] the name of the authority the scenario tests (e.g. "agrovoc_direct")
|
23
|
+
# @param scenarios_config [Hash] configurations from the yml file for all scenarios for an authority
|
24
|
+
def initialize(authority:, authority_name:, scenarios_config:)
|
25
|
+
@authority = authority
|
26
|
+
@authority_name = authority_name
|
27
|
+
@scenarios_config = scenarios_config
|
28
|
+
parse_term_scenarios
|
29
|
+
parse_search_scenarios
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parse_term_scenarios
|
35
|
+
@term_scenarios = []
|
36
|
+
term_scenarios_config.each do |term_scenario_config|
|
37
|
+
@term_scenarios << TermScenario.new(authority: authority,
|
38
|
+
authority_name: authority_name,
|
39
|
+
authority_scenario_config: authority_scenario_config,
|
40
|
+
scenario_config: term_scenario_config)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_search_scenarios
|
45
|
+
@search_scenarios = []
|
46
|
+
search_scenarios_config.each do |search_scenario_config|
|
47
|
+
@search_scenarios << SearchScenario.new(authority: authority,
|
48
|
+
authority_name: authority_name,
|
49
|
+
authority_scenario_config: authority_scenario_config,
|
50
|
+
scenario_config: search_scenario_config)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def scenarios_config
|
55
|
+
@scenarios_config
|
56
|
+
end
|
57
|
+
|
58
|
+
def authority_scenario_config
|
59
|
+
scenarios_config[AUTHORITY_SCENARIO]
|
60
|
+
end
|
61
|
+
|
62
|
+
def term_scenarios_config
|
63
|
+
scenarios_config[TERM_SCENARIOS] || []
|
64
|
+
end
|
65
|
+
|
66
|
+
def search_scenarios_config
|
67
|
+
scenarios_config[SEARCH_SCENARIOS] || []
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# This class parses the search configuration from the yml file into the parts needed by the search scenario validator.
|
2
|
+
module QaServer
|
3
|
+
class SearchScenario < AuthorityScenario
|
4
|
+
|
5
|
+
# @return [String] query being executed by this scenario
|
6
|
+
attr_reader :query
|
7
|
+
|
8
|
+
# @return [Hash] replacements parameters used to construct the URL executed by this scenario
|
9
|
+
attr_reader :replacements
|
10
|
+
|
11
|
+
# @return [Integer] expected_by_position designates the maximum position in search results of subject_uri, if specified, for this scenario to be considered passing
|
12
|
+
attr_reader :expected_by_position
|
13
|
+
|
14
|
+
# @return [String] subject_uri, if specified, should be in the search results between position 1 and expected_by_position
|
15
|
+
attr_reader :subject_uri
|
16
|
+
|
17
|
+
MAX_RECORDS = '4'.freeze
|
18
|
+
DEFAULT_REPLACEMENTS = { maxRecords: MAX_RECORDS }
|
19
|
+
DEFAULT_POSITION = nil
|
20
|
+
DEFAULT_SUBJECT_URI = nil
|
21
|
+
|
22
|
+
# @param authority [Qa::Authorities::LinkedData::GenericAuthority] the instance of the QA authority
|
23
|
+
# @param authoity_name [Symbol] the name of the authority the scenario tests (e.g. :AGROVOC_DIRECT)
|
24
|
+
# @param authority_scenario_config [Hash] configurations from the yml file that pertain to all scenarios regardless of type
|
25
|
+
# @param scenario_config [Hash] configuration from the yml file that are specific to a search scenario
|
26
|
+
def initialize(authority:, authority_name:, authority_scenario_config:, scenario_config:)
|
27
|
+
super
|
28
|
+
@query = scenario_config['query']
|
29
|
+
@subauthority_name = scenario_config['subauth'] || DEFAULT_SUBAUTH
|
30
|
+
@min_result_size = scenario_config['result_size'] || MIN_EXPECTED_SIZE
|
31
|
+
@replacements = scenario_config['replacements'] || DEFAULT_REPLACEMENTS
|
32
|
+
@expected_by_position = scenario_config['position'] || DEFAULT_POSITION
|
33
|
+
@subject_uri = scenario_config['subject_uri'] || DEFAULT_SUBJECT_URI
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate an example URL that can be called in a browser or through curl
|
37
|
+
# @return [String] the example URL
|
38
|
+
def url
|
39
|
+
subauth = "/#{subauthority_name}" if subauthority?
|
40
|
+
prefix = "/#{QA_ENGINE_MOUNT}/search/linked_data/#{authority_name.downcase}#{subauth}"
|
41
|
+
"#{prefix}?q=#{query}#{url_replacements}"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Convert replacements hash into URL parameters
|
47
|
+
def url_replacements
|
48
|
+
return "&maxRecords=#{MAX_RECORDS}" unless replacements
|
49
|
+
param_replacements = ""
|
50
|
+
replacements.each { |k, v| param_replacements += "&#{k}=#{v}" }
|
51
|
+
param_replacements
|
52
|
+
end
|
53
|
+
|
54
|
+
def subauthority?
|
55
|
+
subauthority_name.present?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
# This class parses the term configuration from the yml file into the parts needed by the term scenario validator.
|
4
|
+
module QaServer
|
5
|
+
class TermScenario < AuthorityScenario
|
6
|
+
include ERB::Util
|
7
|
+
|
8
|
+
# @return [String] id or uri of the term being fetched by this scenario
|
9
|
+
attr_reader :identifier
|
10
|
+
|
11
|
+
# @param authority [Qa::Authorities::LinkedData::GenericAuthority] the instance of the QA authority
|
12
|
+
# @param authoity_name [Symbol] the name of the authority the scenario tests (e.g. :AGROVOC_DIRECT)
|
13
|
+
# @param authority_scenario_config [Hash] configurations from the yml file that pertain to all scenarios regardless of type
|
14
|
+
# @param scenario_config [Hash] configuration from the yml file that are specific to a term scenario
|
15
|
+
def initialize(authority:, authority_name:, authority_scenario_config:, scenario_config:)
|
16
|
+
super
|
17
|
+
@identifier = scenario_config['identifier']
|
18
|
+
@subauthority_name = scenario_config['subauth'] || DEFAULT_SUBAUTH
|
19
|
+
@min_result_size = scenario_config['min_result_size'] || MIN_EXPECTED_SIZE
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate an example URL that can be called in a browser or through curl
|
23
|
+
# @return [String] the example URL
|
24
|
+
def url
|
25
|
+
subauth = "/#{subauthority_name}" if subauthority_name.present?
|
26
|
+
prefix = "/#{QA_ENGINE_MOUNT}/show/linked_data/#{authority_name.downcase}#{subauth}"
|
27
|
+
"#{prefix}/#{url_identifier}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Convert identifier into URL safe version with encoding if needed.
|
33
|
+
def url_identifier
|
34
|
+
return uri_encode(identifier) if encode?
|
35
|
+
identifier
|
36
|
+
end
|
37
|
+
|
38
|
+
def subauthority?
|
39
|
+
subauthority_name.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def encode?
|
43
|
+
authority.auth_config.term.term_id_expects_uri?
|
44
|
+
end
|
45
|
+
|
46
|
+
def uri_encode(uri)
|
47
|
+
url_encode(uri).gsub(".", "%2E")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This presenter class provides all data needed by the view that show the list of authorities.
|
2
|
+
module QaServer
|
3
|
+
class AuthorityListPresenter
|
4
|
+
def initialize(urls_data:)
|
5
|
+
@urls_data = urls_data
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Array<Hash>] A list of status data for each scenario tested.
|
9
|
+
# @example
|
10
|
+
# { status: :PASS,
|
11
|
+
# status_label: '√',
|
12
|
+
# authority_name: 'LOCNAMES_LD4L_CACHE',
|
13
|
+
# subauthority_name: 'person',
|
14
|
+
# service: 'ld4l_cache',
|
15
|
+
# action: 'search',
|
16
|
+
# url: '/qa/search/linked_data/locnames_ld4l_cache/person?q=mark twain&maxRecords=4',
|
17
|
+
# err_message: '' }
|
18
|
+
def urls_data
|
19
|
+
@urls_data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# This presenter class provides all data needed by the view that checks the status of authorities.
|
2
|
+
module QaServer
|
3
|
+
class CheckStatusPresenter
|
4
|
+
|
5
|
+
# @param authorities_list [Array<String>] a list of all loaded authorities' names
|
6
|
+
# @param status_data [Array<Hash>] a list of status data for each scenario tested
|
7
|
+
def initialize(authorities_list:, connection_status_data:, accuracy_status_data:)
|
8
|
+
@authorities_list = authorities_list
|
9
|
+
@connection_status_data = connection_status_data
|
10
|
+
@accuracy_status_data = accuracy_status_data
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Array<String>] A list of all loaded authorities' names
|
14
|
+
# @example ['AGROVOC_DIRECT', 'AGROVOC_LD4L_CACHE', 'LOCNAMES_LD4L_CACHE']
|
15
|
+
def authorities_list
|
16
|
+
@authorities_list
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<Hash>] A list of status data for each connection scenario tested.
|
20
|
+
# @example
|
21
|
+
# [ { status: :PASS,
|
22
|
+
# status_label: '√',
|
23
|
+
# authority_name: 'LOCNAMES_LD4L_CACHE',
|
24
|
+
# subauthority_name: 'person',
|
25
|
+
# service: 'ld4l_cache',
|
26
|
+
# action: 'search',
|
27
|
+
# url: '/qa/search/linked_data/locnames_ld4l_cache/person?q=mark twain&maxRecords=4',
|
28
|
+
# err_message: '' }, ... ]
|
29
|
+
def connection_status_data
|
30
|
+
@connection_status_data
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array<Hash>] A list of status data for each accuracy scenario tested.
|
34
|
+
# @example
|
35
|
+
# [ { status: :PASS,
|
36
|
+
# status_label: '√',
|
37
|
+
# authority_name: 'LOCNAMES_LD4L_CACHE',
|
38
|
+
# subauthority_name: 'person',
|
39
|
+
# service: 'ld4l_cache',
|
40
|
+
# action: 'search',
|
41
|
+
# expected: 10,
|
42
|
+
# actual: 8,
|
43
|
+
# url: '/qa/search/linked_data/locnames_ld4l_cache/person?q=mark twain&maxRecords=20',
|
44
|
+
# err_message: '' }, ... ]
|
45
|
+
def accuracy_status_data
|
46
|
+
@accuracy_status_data
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Boolean] true if status data exists; otherwise false
|
50
|
+
def connection_status_data?
|
51
|
+
@connection_status_data.present?
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean] true if status data exists; otherwise false
|
55
|
+
def accuracy_status_data?
|
56
|
+
@accuracy_status_data.present?
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
|
60
|
+
def status_style_class(status)
|
61
|
+
"status-#{status[:status].to_s}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def value_all_collections
|
65
|
+
CheckStatusController::ALL_AUTHORITIES
|
66
|
+
end
|
67
|
+
|
68
|
+
def value_check_param
|
69
|
+
AuthorityValidationController::VALIDATION_TYPE_PARAM
|
70
|
+
end
|
71
|
+
|
72
|
+
def value_check_connections
|
73
|
+
AuthorityValidationController::VALIDATE_CONNECTIONS
|
74
|
+
end
|
75
|
+
|
76
|
+
def label_check_connections
|
77
|
+
"#{value_check_param}_#{value_check_connections}".downcase.to_sym
|
78
|
+
end
|
79
|
+
|
80
|
+
def value_check_accuracy
|
81
|
+
AuthorityValidationController::VALIDATE_ACCURACY
|
82
|
+
end
|
83
|
+
|
84
|
+
def label_check_accuracy
|
85
|
+
"#{value_check_param}_#{value_check_accuracy}".downcase.to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
def value_all_checks
|
89
|
+
AuthorityValidationController::ALL_VALIDATIONS
|
90
|
+
end
|
91
|
+
|
92
|
+
def label_all_checks
|
93
|
+
"#{value_check_param}_#{value_all_checks}".downcase.to_sym
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# This presenter class provides all data needed by the view that monitors status of authorities.
|
2
|
+
module QaServer
|
3
|
+
class MonitorStatusPresenter
|
4
|
+
|
5
|
+
# @param authority_count [Integer] number of loaded authorities
|
6
|
+
# @param authority_status [AuthorityStatus] summary status of the latest run of test scenarios
|
7
|
+
# @param current_data [Array<Hash>] current set of failures for the latest test run, if any
|
8
|
+
# @param historical_data [Array<Hash>] data for past failures
|
9
|
+
def initialize(authority_count:, authority_status:, current_data:, historical_data:)
|
10
|
+
@authority_count = authority_count
|
11
|
+
@authority_status = authority_status
|
12
|
+
@current_failures = current_data
|
13
|
+
@history = historical_data
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [String] date of last test run
|
17
|
+
def last_updated
|
18
|
+
@authority_status.dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String] date of first recorded test run
|
22
|
+
def first_updated
|
23
|
+
AuthorityStatus.first.dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Integer] number of loaded authorities
|
27
|
+
def authorities_count
|
28
|
+
@authority_count
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Integer] number of authorities with failing tests in the latest test run
|
32
|
+
def failing_authorities_count
|
33
|
+
@current_failures.map { |f| f[:authority_name] }.uniq.count
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String] css style class representing whether all tests passed or any failed
|
37
|
+
def authorities_count_style
|
38
|
+
failures? ? 'status-bad' : 'status-good'
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Integer] number of tests in the latest test run
|
42
|
+
def tests_count
|
43
|
+
@authority_status.test_count
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Integer] number of passing tests in the latest test run
|
47
|
+
def passing_tests_count
|
48
|
+
tests_count - failing_tests_count
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Integer] number of failing tests in the latest test run
|
52
|
+
def failing_tests_count
|
53
|
+
@current_failures.count
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] css style class representing whether all tests passed or any failed
|
57
|
+
def failing_tests_style
|
58
|
+
failures? ? 'status-bad' : 'status-good'
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Array<Hash>] A list of failures data in the latest test run, if any
|
62
|
+
# @example
|
63
|
+
# [ { status: :FAIL,
|
64
|
+
# status_label: 'X',
|
65
|
+
# authority_name: 'LOCNAMES_LD4L_CACHE',
|
66
|
+
# subauthority_name: 'person',
|
67
|
+
# service: 'ld4l_cache',
|
68
|
+
# action: 'search',
|
69
|
+
# url: '/qa/search/linked_data/locnames_ld4l_cache/person?q=mark twain&maxRecords=4',
|
70
|
+
# err_message: 'Exception: Something went wrong.' }, ... ]
|
71
|
+
def failures
|
72
|
+
@current_failures
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Boolean] true if failure data exists for the latest test run; otherwise false
|
76
|
+
def failures?
|
77
|
+
failing_tests_count.positive?
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [Array<Hash>] historical test data to be displayed
|
81
|
+
# @example
|
82
|
+
# [ { days_failing: 1, authority_name: 'AGROVOC_DIRECT' },
|
83
|
+
# { days_failing: 3, authority_name: 'AGROVOC_LD4L_CACHE' },
|
84
|
+
# { days_failing: 0, authority_name: 'LOCNAMES_LD4L_CACHE' } ]
|
85
|
+
def history
|
86
|
+
# TODO: STUBED -- need to include history of past failures -- Question: How much data to save?
|
87
|
+
# Want to answer questions like...
|
88
|
+
# * # of failing days out of # of days run
|
89
|
+
# * # type of failures... can't load authority vs. exception vs. no data returned
|
90
|
+
# * for a given authority, did all tests fail or just a few?
|
91
|
+
# * are tests failing for a particular subauthority?
|
92
|
+
# * are tests failing for a particular type (search vs. term)
|
93
|
+
history = []
|
94
|
+
entry = { days_failing: 3,
|
95
|
+
authority_name: "FOO",
|
96
|
+
subauthority_name: "bar",
|
97
|
+
service: 'test',
|
98
|
+
action: 'search' }
|
99
|
+
history << entry
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Boolean] true if historical test data exists; otherwise false
|
103
|
+
def history?
|
104
|
+
# TODO: STUBED -- need check for history data
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Provide service methods for getting a list of all authorities and scenarios for an authority.
|
2
|
+
module QaServer
|
3
|
+
class AuthorityListerService
|
4
|
+
|
5
|
+
# Return a list of supported authorities
|
6
|
+
# @return [Array<String>] list of authorities
|
7
|
+
def self.authorities_list
|
8
|
+
LINKED_DATA_AUTHORITIES_CONFIG.keys.sort
|
9
|
+
end
|
10
|
+
|
11
|
+
# Fill in status_log with data about each scenario for an authority
|
12
|
+
# @param authority_name [String] the name of the authority
|
13
|
+
# @param status_log [ScenarioLogger] the log that will hold the data about the scenarios
|
14
|
+
def self.scenarios_list(authority_name:, status_log:)
|
15
|
+
scenarios = QaServer::ScenariosLoaderService.load(authority_name: authority_name, status_log: status_log)
|
16
|
+
return if scenarios.blank?
|
17
|
+
list_terms(scenarios, status_log)
|
18
|
+
list_searches(scenarios, status_log)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.list_terms(scenarios, status_log)
|
24
|
+
scenarios.term_scenarios.each { |scenario| QaServer::TermScenarioValidator.new(scenario: scenario, status_log: status_log).log_without_running }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.list_searches(scenarios, status_log)
|
28
|
+
scenarios.search_scenarios.each { |scenario| QaServer::SearchScenarioValidator.new(scenario: scenario, status_log: status_log).log_without_running }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This class loads an authority.
|
2
|
+
module QaServer
|
3
|
+
class AuthorityLoaderService
|
4
|
+
|
5
|
+
# Load a QA authority
|
6
|
+
# @param authority_name [String] name of the authority to load (e.g. "agrovoc_direct")
|
7
|
+
# @param status_log [ScenarioLogger] logger to hold failure information if the authority cannot be loaded
|
8
|
+
# @return [Qa::Authorities::LinkedData::GenericAuthority] the instance of the authority that can receive QA requests OR nil if fails to load
|
9
|
+
def self.load(authority_name:, status_log:)
|
10
|
+
begin
|
11
|
+
authority = load_authority(authority_name, status_log)
|
12
|
+
return nil if authority.blank?
|
13
|
+
rescue Exception => e
|
14
|
+
status_log.add(authority_name: authority_name,
|
15
|
+
status: ScenarioValidator::FAIL,
|
16
|
+
error_message: "Unable to load authority '#{authority_name}'; cause: #{e.message}")
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
authority
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def self.authority_key(authority_name)
|
24
|
+
authority_name.upcase.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_authority(authority_name, status_log)
|
28
|
+
authority = Qa::Authorities::LinkedData::GenericAuthority.new(authority_key(authority_name))
|
29
|
+
if authority.blank?
|
30
|
+
status_log.add(authority_name: authority_name,
|
31
|
+
status: ScenarioValidator::FAIL,
|
32
|
+
error_message: "Unable to load authority '#{authority_name}'; cause: UNKNOWN") unless authority.present?
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
authority
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Provide service methods for running a a set of validation scenarios for an authority.
|
2
|
+
module QaServer
|
3
|
+
class AuthorityValidatorService
|
4
|
+
class_attribute :validator_class,
|
5
|
+
:term_validator_class,
|
6
|
+
:search_validator_class,
|
7
|
+
:scenarios_loader_class
|
8
|
+
|
9
|
+
self.validator_class = ScenarioValidator
|
10
|
+
self.term_validator_class = TermScenarioValidator
|
11
|
+
self.search_validator_class = SearchScenarioValidator
|
12
|
+
self.scenarios_loader_class = ScenariosLoaderService
|
13
|
+
|
14
|
+
VALIDATE_CONNECTIONS = validator_class::VALIDATE_CONNECTION
|
15
|
+
VALIDATE_ACCURACY = validator_class::VALIDATE_ACCURACY
|
16
|
+
ALL_VALIDATIONS = validator_class::ALL_VALIDATIONS
|
17
|
+
DEFAULT_VALIDATION_TYPE = validator_class::DEFAULT_VALIDATION_TYPE
|
18
|
+
|
19
|
+
# Run the set of validation scenarios for an authority logging the results
|
20
|
+
# @param authority_name [String] the name of the authority
|
21
|
+
# @param status_log [ScenarioLogger] the log that will hold the data about the scenarios and test results
|
22
|
+
# @param validation_type [Symbol] the type of scenarios to run (e.g. VALIDATE_CONNECTIONS, VALIDATE_ACCURACY, ALL_VALIDATIONS)
|
23
|
+
def self.run(authority_name:, status_log:, validation_type: DEFAULT_VALIDATION_TYPE)
|
24
|
+
scenarios = scenarios_loader_class.load(authority_name: authority_name, status_log: status_log)
|
25
|
+
return if scenarios.blank?
|
26
|
+
run_terms(scenarios, status_log, validation_type)
|
27
|
+
run_searches(scenarios, status_log, validation_type)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.run_terms(scenarios, status_log, validation_type)
|
34
|
+
scenarios.term_scenarios.each { |scenario| term_validator_class.new(scenario: scenario, status_log: status_log, validation_type: validation_type).run }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.run_searches(scenarios, status_log, validation_type)
|
38
|
+
scenarios.search_scenarios.each { |scenario| search_validator_class.new(scenario: scenario, status_log: status_log, validation_type: validation_type).run }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module QaServer
|
4
|
+
# QaServer::DatabaseMigrator is responsible for copying QaServer's required database
|
5
|
+
# migrations into applications. Rails engines typically use the built-in
|
6
|
+
# `{ENGINE_NAME}:install:migrations` task to handle this; instead QaServer
|
7
|
+
# follows the practice used by Devise to dynamically subclass migrations with
|
8
|
+
# the version of `ActiveRecord::Migration` corresponding to the version of
|
9
|
+
# Rails used by the application.
|
10
|
+
#
|
11
|
+
# @note QaServer::DatabaseMigrator uses Rails' generator internals to avoid
|
12
|
+
# having to re-implement code that knows how to copy migrations only if
|
13
|
+
# needed
|
14
|
+
class DatabaseMigrator < Rails::Generators::Base
|
15
|
+
# @note included to pick up AR's migration numbering algorithm
|
16
|
+
include ActiveRecord::Generators::Migration
|
17
|
+
|
18
|
+
# @note This method is required by Rails::Generators::Base
|
19
|
+
def self.source_root
|
20
|
+
migrations_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.migrations_dir
|
24
|
+
QaServer::Engine.root.join('lib', 'generators', 'qa_server', 'templates')
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.copy
|
28
|
+
new.copy
|
29
|
+
end
|
30
|
+
|
31
|
+
# def copy
|
32
|
+
# # QA Server's migrations changed between 2.0.0 and subsequent versions, so the
|
33
|
+
# # migration comparison algorithm decides that those older migrations are a
|
34
|
+
# # source of conflict. Default Rails behavior here is to abort and
|
35
|
+
# # explicitly instruct the user to try again with either the `--skip` or
|
36
|
+
# # `--force` option. QA Server skips these conflicts.
|
37
|
+
# migrations.each do |filename|
|
38
|
+
# migration_template filename,
|
39
|
+
# "db/migrate/#{parse_basename_from(filename)}",
|
40
|
+
# migration_version: migration_version,
|
41
|
+
# skip: true
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
def copy
|
45
|
+
migrations.each do |filename|
|
46
|
+
migration_template filename,
|
47
|
+
"db/migrate/#{parse_basename_from(filename)}",
|
48
|
+
migration_version: migration_version
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def migrations
|
55
|
+
Dir.chdir(self.class.migrations_dir) { Dir.glob('db/migrate/[0-9]*_*.rb.erb') }.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_basename_from(filename)
|
59
|
+
# Add engine name to filename to mimic ``ActiveRecord::Migration.copy` behavior
|
60
|
+
filename.slice(/(?<dateprefix>\d)+_(?<basename>.+)\.erb/, 'basename').sub('.', '.qa_server.')
|
61
|
+
end
|
62
|
+
|
63
|
+
def migration_version
|
64
|
+
# Don't use AR migration versioning in Rails 4
|
65
|
+
return if Rails.version < '5.0.0'
|
66
|
+
# Specify the current major.minor version of Rails for AR migrations
|
67
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# This class loads scenario configuration file for an authority.
|
2
|
+
module QaServer
|
3
|
+
class ScenariosLoaderService
|
4
|
+
|
5
|
+
# Load scenarios for testing an authority
|
6
|
+
# @param authority_name [String] name of the authority to load (e.g. "agrovoc_direct")
|
7
|
+
# @param status_log [ScenarioLogger] logger to hold failure information if the scenarios cannot be loaded
|
8
|
+
# @return [Scenarios] the instance of the set of scenarios to test for the authority OR nil if fails to load
|
9
|
+
def self.load(authority_name:, status_log:)
|
10
|
+
begin
|
11
|
+
authority = load_authority(authority_name, status_log)
|
12
|
+
return nil if authority.blank?
|
13
|
+
return nil unless scenarios_exist?(authority_name, status_log)
|
14
|
+
|
15
|
+
scenarios_config = load_config(authority_name, status_log)
|
16
|
+
return nil if scenarios_config.blank?
|
17
|
+
|
18
|
+
scenarios = Scenarios.new(authority: authority, authority_name: authority_name, scenarios_config: scenarios_config)
|
19
|
+
rescue Exception => e
|
20
|
+
status_log.add(authority_name: authority_name,
|
21
|
+
status: ScenarioValidator::FAIL,
|
22
|
+
error_message: "Unable to load scenarios for authority '#{authority_name}'; cause: #{e.message}")
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
scenarios
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def self.load_authority(authority_name, status_log)
|
31
|
+
AuthorityLoaderService.load(authority_name: authority_name, status_log: status_log)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.load_config(authority_name, status_log)
|
35
|
+
scenarios_config = YAML.load_file(scenario_path(authority_name))
|
36
|
+
unless scenarios_config.present?
|
37
|
+
status_log.add(authority_name: authority_name,
|
38
|
+
status: ScenarioValidator::FAIL,
|
39
|
+
error_message: "Unable to load scenarios for authority '#{authority_name}'; cause: UNKNOWN")
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
scenarios_config
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.scenarios_exist?(authority_name, status_log)
|
46
|
+
return true if File.exists?(scenario_path(authority_name))
|
47
|
+
status_log.add(authority_name: authority_name,
|
48
|
+
status: ScenarioValidator::FAIL,
|
49
|
+
error_message: "Unable to load scenarios for authority '#{authority_name}'; cause: #{scenario_path} does not exist.")
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.scenario_path(authority_name)
|
54
|
+
File.join(::Rails.root, 'config', 'authorities', 'linked_data', 'scenarios', "#{authority_name.downcase}_validation.yml")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|