diff --git a/.rubocop.yml b/.rubocop.yml index 1b52913a..cc572bf8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,6 +9,9 @@ AllCops: - vendor/**/* NewCops: enable +Layout/IndentationWidth: + EnforcedStyleAlignWith: relative_to_receiver + Rails/InverseOf: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index b1a702f4..8f63b9cc 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit` -# on 2025-08-27 14:16:51 UTC using RuboCop version 1.79.2. +# on 2026-03-23 09:57:06 UTC using RuboCop version 1.85.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -21,7 +21,7 @@ Layout/ExtraSpacing: Exclude: - 'test/controllers/users_controller_test.rb' -# Offense count: 31 +# Offense count: 32 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces @@ -49,13 +49,6 @@ Lint/EmptyBlock: - 'Gemfile' - 'config/routes.rb' -# Offense count: 2 -# Configuration parameters: AllowComments. -Lint/EmptyClass: - Exclude: - - 'app/models/presenter.rb' - - 'test/test_helper.rb' - # Offense count: 3 # Configuration parameters: AllowedParentClasses. Lint/MissingSuper: @@ -71,7 +64,7 @@ Lint/ParenthesesAsGroupedExpression: # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'app/controllers/qc_assets_controller.rb' @@ -81,7 +74,7 @@ Lint/UnusedBlockArgument: # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. # NotImplementedExceptions: NotImplementedError Lint/UnusedMethodArgument: Exclude: @@ -89,14 +82,12 @@ Lint/UnusedMethodArgument: - 'app/models/gatekeeper/robot.rb' - 'app/models/presenter/lot.rb' -# Offense count: 4 +# Offense count: 3 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect. Lint/UselessAssignment: Exclude: - 'app/helpers/application_helper.rb' - 'app/models/gatekeeper/robot.rb' - - 'lib/tasks/config.rake' # Offense count: 21 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. @@ -214,8 +205,6 @@ Rails/Delegate: - 'test/mock_api.rb' # Offense count: 5 -# Configuration parameters: Include. -# Include: **/app/controllers/**/*.rb, **/app/mailers/**/*.rb Rails/LexicallyScopedActionFilter: Exclude: - 'app/controllers/assets_controller.rb' @@ -271,21 +260,6 @@ Style/IfUnlessModifier: - 'lib/tasks/config.rake' - 'test/mock_api.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EnforcedStyle. -# SupportedStyles: line_count_dependent, lambda, literal -Style/Lambda: - Exclude: - - 'lib/tasks/config.rake' - -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: . -# SupportedStyles: call, braces -Style/LambdaCall: - EnforcedStyle: braces - # Offense count: 5 Style/MissingRespondToMissing: Exclude: @@ -344,6 +318,12 @@ Style/PreferredHashMethods: - 'app/models/qc_asset_creator/plate_conversion.rb' - 'test/mock_api.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ReduceToHash: + Exclude: + - 'app/models/qc_asset_creator/multiple_tag2_conversion.rb' + # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Style/StderrPuts: @@ -391,9 +371,9 @@ Style/ZeroLengthPredicate: - 'app/models/gatekeeper/plate.rb' - 'app/models/gatekeeper/tube.rb' -# Offense count: 78 +# Offense count: 76 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: Exclude: diff --git a/.ruby-version b/.ruby-version index 7921bd0c..7bcbb380 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.8 +3.4.9 diff --git a/Gemfile.lock b/Gemfile.lock index ff2d8288..2984b830 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -88,14 +88,14 @@ GEM mutex_m securerandom (>= 0.3) tzinfo (~> 2.0) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.8.9) + public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) autoprefixer-rails (10.4.7.0) execjs (~> 2) base64 (0.3.0) benchmark (0.5.0) - bigdecimal (4.0.1) + bigdecimal (4.1.0) bootsnap (1.23.0) msgpack (~> 1.2) bootstrap-datepicker-rails (1.10.0.1) @@ -152,7 +152,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.18.1) + json (2.19.3) json_api_client (1.23.0) activemodel (>= 6.0.0) activesupport (>= 6.0.0) @@ -191,7 +191,7 @@ GEM minitest-rails-capybara (0.0.2) capybara minitest-rails - mocha (3.0.2) + mocha (3.1.0) ruby2_keywords (>= 0.0.5) msgpack (1.8.0) multi_json (1.15.0) @@ -214,12 +214,12 @@ GEM racc (~> 1.4) nokogiri (1.19.1-x86_64-linux-gnu) racc (~> 1.4) - oj (3.16.15) + oj (3.16.16) bigdecimal (>= 3.0) ostruct (>= 0.2) ostruct (0.6.3) parallel (1.27.0) - parser (3.3.10.1) + parser (3.3.11.1) ast (~> 2.4.1) racc prism (1.9.0) @@ -228,11 +228,11 @@ GEM method_source (~> 1.0) pry-rails (0.3.11) pry (>= 0.13.0) - public_suffix (6.0.1) + public_suffix (7.0.5) puma (7.2.0) nio4r (~> 2.0) racc (1.8.1) - rack (2.2.22) + rack (2.2.23) rack-test (2.2.0) rack (>= 1.3) rails (7.0.10) @@ -277,7 +277,7 @@ GEM roo (2.8.3) nokogiri (~> 1) rubyzip (>= 1.3.0, < 3.0.0) - rubocop (1.84.1) + rubocop (1.86.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -288,7 +288,7 @@ GEM rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.49.0) + rubocop-ast (1.49.1) parser (>= 3.3.7.2) prism (~> 1.7) rubocop-rails (2.34.3) diff --git a/README.md b/README.md index e517dfb4..22135250 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Ruby Tests](https://github.com/sanger/gatekeeper/actions/workflows/ruby_test.yml/badge.svg)](https://github.com/sanger/gatekeeper/actions/workflows/ruby_test.yml) [![codecov](https://codecov.io/gh/sanger/gatekeeper/graph/badge.svg?token=VbxDtCNFAh)](https://codecov.io/gh/sanger/gatekeeper) -# Gatekeeper +# Reflected K Logo Gatekeeper Gatekeeper is used to track the production and validation of batches of tag plates for sequencing. It is designed to interface with the [Sequencescape](https://github.com/sanger/sequencescape) LIMS. diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index a3d7d420..eadf8b63 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,2 +1,3 @@ //= link application.css //= link application.js +//= link gate-kk.svg diff --git a/app/assets/images/gate-kk.svg b/app/assets/images/gate-kk.svg new file mode 100644 index 00000000..8068c60d --- /dev/null +++ b/app/assets/images/gate-kk.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + diff --git a/app/assets/stylesheets/gatekeeper.scss b/app/assets/stylesheets/gatekeeper.scss index 99ded5d1..89b4ad02 100644 --- a/app/assets/stylesheets/gatekeeper.scss +++ b/app/assets/stylesheets/gatekeeper.scss @@ -24,6 +24,12 @@ h1 { padding: 0.5em; } +.logo { + margin-bottom: -11px; + margin-right: -11px; + vertical-align: baseline; +} + .gk-section { text-align: justify; } diff --git a/app/models/presenter.rb b/app/models/presenter.rb deleted file mode 100644 index a20e4c18..00000000 --- a/app/models/presenter.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -## -# Presenters are used to represent objects to the view model -# This provides a few universal helper methods -class Presenter -end diff --git a/app/views/pages/index.erb b/app/views/pages/index.erb index a4779d32..8eff547a 100644 --- a/app/views/pages/index.erb +++ b/app/views/pages/index.erb @@ -1,5 +1,8 @@
-

<%= Gatekeeper::Application.config.name %>

+

+ <%= image_tag 'gate-kk.svg', alt: 'Reflected K Logo', class: 'logo'%> + <%= Gatekeeper::Application.config.name %> +

Register Lots
diff --git a/config/application.rb b/config/application.rb index b987323b..fd5f673f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -34,7 +34,7 @@ class Application < Rails::Application config.i18n.fallbacks = [I18n.default_locale] # Ensures precompiling is faster by not loading the application - config.assets.initialize_on_precompile = false + config.assets.initialize_on_precompile = true config.disable_animations = false config.destroyable_states = %w[pending available] diff --git a/config/initializers/request_logger.rb b/config/initializers/request_logger.rb new file mode 100644 index 00000000..3a94825a --- /dev/null +++ b/config/initializers/request_logger.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative '../../lib/middleware/request_logger' + +Rails.application.configure do + # Insert RequestLogger near the top, before Rails::Rack::Logger + config.middleware.insert_before(Rails::Rack::Logger, Middleware::RequestLogger) +end + +# Add backtrace silencers for middleware so that we don't see it in backtraces. +Rails.backtrace_cleaner.add_silencer { |line| line.include?('request_logger') } diff --git a/lib/middleware/request_logger.rb b/lib/middleware/request_logger.rb new file mode 100644 index 00000000..5942cbc1 --- /dev/null +++ b/lib/middleware/request_logger.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +module Middleware + # Log request and response details for monitoring and high-level profiling. + # + # @param log_level [Symbol] the log level to use for logging requests (default: :info) + # @param environment_context [Hash] additional context to include in log entries, such as host and version information + # + # Returns a JSON parseable log entry like: + # [INFO] [RequestLogger] {"method":"GET","path":"/samples/1234","format":"html","status_code":200, + # "status_message":"OK","duration_ms":935,"client_ip":"172.21.43.210", + # "request_id":"9fd18098-dea3-46f0-83c8-c41852441db3","tags":["request","success"], + # "@timestamp":"2026-02-12T12:10:50.284+00:00"} + class RequestLogger + def initialize(app, log_level: :info, environment_context: nil) + @app = app + @log_level = log_level + @environment_context = environment_context + end + + def call(env) + response, elapsed_ms = elapsed_milliseconds { @app.call(env) } + + request = ActionDispatch::Request.new(env) + log_request(request, response, elapsed_ms) + + response + end + + private + + # Get the current clock time using the Rack::Runtime clock + def clock_time + Rack::Utils.clock_time + end + + def elapsed_milliseconds + start_time = clock_time + result = yield + end_time = clock_time + + elapsed_ms = ((end_time - start_time) * 1000).round + [result, elapsed_ms] + end + + def tag_for_status(status_code) + case status_code + when 100..199 then 'informational' + when 200..299 then 'success' + when 300..399 then 'redirection' + when 400..499 then 'client_error' + when 500..599 then 'server_error' + end + end + + def tags(status_code) + tags = ['request'] + tags << tag_for_status(status_code) + tags.compact! + tags + end + + def log_request(request, response, elapsed_ms) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength + status_code, _headers, _body = response + + status_message = Rack::Utils::HTTP_STATUS_CODES[status_code] || 'Unknown Status' + timestamp = Time.zone.now.iso8601(3) + + record = { + method: request.request_method, + url: request.fullpath, + path: request.path, + format: request.format.symbol, + status_code: status_code, + status_message: status_message, + duration_ms: elapsed_ms, + client_ip: request.remote_ip, + request_id: request.request_id, + tags: tags(status_code), + '@timestamp': timestamp + } + record.merge!(@environment_context) if @environment_context.present? + + Rails.logger.public_send(@log_level, "[RequestLogger] #{record.to_json}") + end + end +end diff --git a/public/favicon.ico b/public/favicon.ico index e69de29b..b29d025f 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/test/test_helper.rb b/test/test_helper.rb index 60ca81cd..17872f04 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -66,10 +66,3 @@ class ActionDispatch::IntegrationTest Capybara.default_driver = :headless_chrome Capybara.default_max_wait_time = 5 - -class ActiveSupport::TestCase - # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. - # fixtures :all - - # Add more helper methods to be used by all tests here... -end