-
Notifications
You must be signed in to change notification settings - Fork 34
Y26-105 - [PR] [Bug] useq_wafer dead-letters on ware-prod #5650
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
bb460c9
cbef03e
05a4146
71ac878
623db61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,9 @@ class Descriptor < ApplicationRecord | |
| belongs_to :task | ||
| serialize :selection, coder: YAML | ||
|
|
||
| DATE_YEAR_MIN = 1990 | ||
| DATE_YEAR_MAX = 2100 | ||
|
|
||
| def is_required? | ||
| required | ||
| end | ||
|
|
@@ -11,4 +14,40 @@ def matches?(search) | |
| search.descriptors.each { |descriptor| return true if descriptor.name == name && descriptor.value == value } | ||
| false | ||
| end | ||
|
|
||
| # Returns an array of validation errors for the submitted descriptor value. | ||
| # The value comes from the Task Details form for a workflow task on a batch. | ||
| # @return [Array] An array of error messages, empty if the value is valid | ||
| def validate_value(submitted_value) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Can you move these to active model validations so you can call descriptor.valid? |
||
| return ["#{name} is required"] if submitted_value.blank? && is_required? | ||
| return [] if submitted_value.blank? | ||
| return validate_date_value(submitted_value) if kind == 'Date' | ||
|
|
||
| [] | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Validates that the submitted value is a valid date string in the format | ||
| # YYYY-MM-DD, and that the year is within a reasonable range. | ||
| # @return [Array] An array of error messages, empty if the value is valid | ||
| def validate_date_value(submitted_value) | ||
| unless submitted_value.match?(/\A\d{4}-\d{2}-\d{2}\z/) | ||
| return ["'#{submitted_value}' is not a valid date for #{name} (expected YYYY-MM-DD)"] | ||
| end | ||
|
|
||
| parsed_date = Date.iso8601(submitted_value) | ||
| validate_date_year(parsed_date) | ||
| rescue ArgumentError | ||
| ["'#{submitted_value}' is not a valid date for #{name} (expected YYYY-MM-DD)"] | ||
| end | ||
|
|
||
| # Validates that the year of the submitted date is within a reasonable range | ||
| # to catch common data entry errors (e.g. 62026 instead of 2026). | ||
| # @return [Array] An array of error messages, empty if the year is valid | ||
| def validate_date_year(parsed_date) | ||
| return [] if parsed_date.year.between?(DATE_YEAR_MIN, DATE_YEAR_MAX) | ||
|
|
||
| ["Date year for #{name} must be between #{DATE_YEAR_MIN} and #{DATE_YEAR_MAX} (got #{parsed_date.year})"] | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,9 @@ def render | |
| end | ||
|
|
||
| def perform | ||
| errors = validate_descriptor_inputs | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Can we use rails validations here. Make use of the active record errors behaviour and add errors to the task model by running task.valid?. You would need to set validations on descriptor too for this to work I think. |
||
| return [false, errors.join(', ')] if errors.any? | ||
|
|
||
| # Process each request that has been selected in the front end | ||
| # by default all requests are selected, but in rare circumstances the user | ||
| # can uncheck a request to exclude it from the step | ||
|
|
@@ -28,10 +31,39 @@ def perform | |
|
|
||
| private | ||
|
|
||
| # Iterates every descriptor on the task and asks each one to validate the | ||
| # submitted value. | ||
| # @return [Array<String>] An array of error messages, empty if all values are valid | ||
| def validate_descriptor_inputs | ||
| return [] if task.descriptors.empty? | ||
|
|
||
| selected_requests_for_validation.each_with_object([]) do |request, errors| | ||
| errors.concat(descriptor_errors_for(request)) | ||
| end.uniq | ||
| end | ||
|
|
||
| def params | ||
| @params.respond_to?(:permit!) ? @params.permit!.to_h : @params | ||
| end | ||
|
|
||
| # Returns an array of the requests that have been selected in the UI for | ||
| # the Task Details form. | ||
| # @return [Array<Integer>] An array of selected requests | ||
| def selected_requests_for_validation | ||
| requests.select { |request| selected_requests.include?(request.id) } | ||
| end | ||
|
|
||
| # For a given request, returns an array of error messages for any descriptors | ||
| # that fail validation. | ||
| # @return [Array<String>] An array of error messages, empty if all values are valid | ||
| def descriptor_errors_for(request) | ||
| submitted = descriptors(request) | ||
|
|
||
| task.descriptors.each_with_object([]) do |descriptor, errors| | ||
| errors.concat(descriptor.validate_value(submitted[descriptor.name])) | ||
| end | ||
| end | ||
|
|
||
| def process_request(request) | ||
| LabEvent.create!( | ||
| batch: batch, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'rails_helper' | ||
|
|
||
| RSpec.describe Descriptor do | ||
| describe '#validate_value' do | ||
| subject(:errors) { descriptor.validate_value(value) } | ||
|
|
||
| context 'when kind is Date' do | ||
| context 'when required is true' do | ||
| let(:descriptor) { described_class.new(name: 'OTR carrier expiry', kind: 'Date', required: true) } | ||
|
|
||
| context 'with a valid ISO 8601 date' do | ||
| let(:value) { '2026-06-01' } | ||
|
|
||
| it { is_expected.to be_empty } | ||
| end | ||
|
|
||
| context 'with a blank value' do | ||
| let(:value) { '' } | ||
|
|
||
| it { is_expected.to contain_exactly('OTR carrier expiry is required') } | ||
| end | ||
|
|
||
| context 'with an invalid date string' do | ||
| let(:value) { 'not-a-date' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| "'not-a-date' is not a valid date for OTR carrier expiry (expected YYYY-MM-DD)" | ||
| ) | ||
| } | ||
| end | ||
|
|
||
| context 'with a date in the wrong format (DD/MM/YYYY)' do | ||
| let(:value) { '01/06/2026' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| "'01/06/2026' is not a valid date for OTR carrier expiry (expected YYYY-MM-DD)" | ||
| ) | ||
| } | ||
| end | ||
|
|
||
| context 'with a year too far in the past' do | ||
| let(:value) { '1989-06-01' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| 'Date year for OTR carrier expiry must be between 1990 and 2100 (got 1989)' | ||
| ) | ||
| } | ||
| end | ||
|
|
||
| context 'with a year too far in the future' do | ||
| let(:value) { '2101-06-01' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| 'Date year for OTR carrier expiry must be between 1990 and 2100 (got 2101)' | ||
| ) | ||
| } | ||
| end | ||
|
|
||
| context 'with a typo in the year (e.g., 62026 instead of 2026)' do | ||
| let(:value) { '62026-06-01' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| "'62026-06-01' is not a valid date for OTR carrier expiry (expected YYYY-MM-DD)" | ||
| ) | ||
| } | ||
| end | ||
| end | ||
|
|
||
| context 'when required is false' do | ||
| let(:descriptor) { described_class.new(name: 'OTR carrier expiry', kind: 'Date', required: false) } | ||
|
|
||
| context 'with a blank value' do | ||
| let(:value) { '' } | ||
|
|
||
| it { is_expected.to be_empty } | ||
| end | ||
|
|
||
| context 'with a valid ISO 8601 date' do | ||
| let(:value) { '2026-06-01' } | ||
|
|
||
| it { is_expected.to be_empty } | ||
| end | ||
|
|
||
| context 'with an invalid date string' do | ||
| let(:value) { 'not-a-date' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| "'not-a-date' is not a valid date for OTR carrier expiry (expected YYYY-MM-DD)" | ||
| ) | ||
| } | ||
| end | ||
|
|
||
| context 'with a year sanity check failure' do | ||
| let(:value) { '62026-06-01' } | ||
|
|
||
| it { | ||
| is_expected.to contain_exactly( | ||
| "'62026-06-01' is not a valid date for OTR carrier expiry (expected YYYY-MM-DD)" | ||
| ) | ||
| } | ||
| end | ||
| end | ||
| end | ||
|
|
||
| context 'when kind is Text' do | ||
| let(:descriptor) { described_class.new(name: 'Comment', kind: 'Text', required: false) } | ||
| let(:value) { 'any free text' } | ||
|
|
||
| it { is_expected.to be_empty } | ||
| end | ||
|
|
||
| context 'when kind is Selection' do | ||
| let(:descriptor) { described_class.new(name: 'Workflow', kind: 'Selection', required: false) } | ||
| let(:value) { 'Standard' } | ||
|
|
||
| it { is_expected.to be_empty } | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to stop cases like the dead letter where we have 62026 as the year?