Skip to content

Commit

Permalink
Merge pull request #2135 from sanger/Y24-190-use-v2-for-qcables
Browse files Browse the repository at this point in the history
Y24-190: Use SS API V2 for Qcable resources
  • Loading branch information
sdjmchattie authored Jan 13, 2025
2 parents 6e41de4 + c52999d commit 87b191a
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 37 deletions.
9 changes: 6 additions & 3 deletions app/controllers/tag_plates_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# frozen_string_literal: true

# Receives AJAX requests when creating tag plates, returns the
# plate information eg. lot number, template, status
# plate information eg. lot number, template
# The front end makes a decision regarding suitability
class TagPlatesController < ApplicationController
INCLUDES = [:labware, { lot: [{ lot_type: :target_purpose }, :template] }].freeze

def show
qcable = Presenters::QcablePresenter.new(api.qcable.find(params[:id]))
respond_to { |format| format.json { render json: { 'qcable' => qcable } } }
qcable_resource = Sequencescape::Api::V2::Qcable.includes(*INCLUDES).find(uuid: params[:id]).first
qcable_presenter = Presenters::QcablePresenter.new(qcable_resource)
respond_to { |format| format.json { render json: { 'qcable' => qcable_presenter } } }
end
end
32 changes: 24 additions & 8 deletions app/models/presenters/qcable_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,31 @@
module Presenters
# Used for tag plates / tag 2 tubes. Rendered with default to_json behaviour.
class QcablePresenter
def initialize(qcable) # rubocop:todo Metrics/AbcSize
@uuid = qcable.uuid
@tag_layout = qcable.lot.template_name
@asset_uuid = qcable.asset.uuid
def initialize(qcable)
init_qcable_attributes(qcable)
init_lot_attributes(qcable.lot)
init_lot_type_attributes(qcable.lot.lot_type)
init_template_attributes(qcable.lot.template)
end

def init_qcable_attributes(qcable)
@asset_uuid = qcable.labware.uuid
@state = qcable.state
@type = qcable.lot.lot_type_name
@qcable_type = qcable.lot.lot_type.qcable_name
@template_uuid = qcable.lot.template.uuid
@lot_number = qcable.lot.lot_number
@uuid = qcable.uuid
end

def init_lot_attributes(lot)
@lot_number = lot.lot_number
end

def init_lot_type_attributes(lot_type)
@qcable_type = lot_type.target_purpose.name
@type = lot_type.name
end

def init_template_attributes(template)
@tag_layout = template.name
@template_uuid = template.uuid
end
end
end
10 changes: 10 additions & 0 deletions app/sequencescape/sequencescape/api/v2/lot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

# A Lot from sequencescape via the V2 API
class Sequencescape::Api::V2::Lot < Sequencescape::Api::V2::Base
has_one :lot_type

# The template is is a polymorphic relationship in Sequencescape, but we only want to access the UUID
# and so we don't need a specific class since all properties are accessible via the base class.
has_one :template, class_name: 'Sequencescape::Api::V2::Base'
end
6 changes: 6 additions & 0 deletions app/sequencescape/sequencescape/api/v2/lot_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

# A LotType from sequencescape via the V2 API
class Sequencescape::Api::V2::LotType < Sequencescape::Api::V2::Base
has_one :target_purpose, class_name: 'Sequencescape::Api::V2::Purpose'
end
1 change: 0 additions & 1 deletion app/sequencescape/sequencescape/api/v2/purpose.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# frozen_string_literal: true

class Sequencescape::Api::V2::Purpose < Sequencescape::Api::V2::Base
UNKNOWN = 'UNKNOWN'
end
7 changes: 7 additions & 0 deletions app/sequencescape/sequencescape/api/v2/qcable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

# A Qcable from sequencescape via the V2 API
class Sequencescape::Api::V2::Qcable < Sequencescape::Api::V2::Base
has_one :labware
has_one :lot
end
47 changes: 47 additions & 0 deletions spec/factories/qcable_factories.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,53 @@
# frozen_string_literal: true

FactoryBot.define do
# API V2 QCable
factory :v2_qcable, class: Sequencescape::Api::V2::Qcable, traits: [:barcoded_v2] do
skip_create

uuid
state { 'available' }

transient do
labware { create :v2_plate }
lot { create :v2_lot }
end

after(:build) do |qcable, factory|
qcable._cached_relationship(:labware) { factory.labware } if factory.labware
qcable._cached_relationship(:lot) { factory.lot } if factory.lot
qcable._cached_relationship(:asset) { factory.labware } if factory.labware # alias for labware
end
end

factory :v2_lot, class: Sequencescape::Api::V2::Lot do
skip_create

sequence(:lot_number) { |n| "UAT12345.#{n}" }

transient do
lot_type { create :v2_lot_type }
template { create :v2_tag_layout_template }
end

after(:build) do |lot, factory|
lot._cached_relationship(:lot_type) { factory.lot_type } if factory.lot_type
lot._cached_relationship(:template) { factory.template } if factory.template
end
end

factory :v2_lot_type, class: Sequencescape::Api::V2::LotType do
skip_create

sequence(:name) { |n| "LotType#{n}" }

transient { target_purpose { create :v2_purpose } }

after(:build) do |lot_type, factory|
lot_type._cached_relationship(:target_purpose) { factory.target_purpose } if factory.target_purpose
end
end

# API V1 Qcable (Records the QC status of eg. a tag plate)
factory :qcable, class: Limber::Qcable, traits: %i[api_object barcoded] do
with_belongs_to_associations 'lot', 'qcable_creator', 'asset'
Expand Down
51 changes: 26 additions & 25 deletions spec/features/creating_a_tag_plate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
purpose_uuid: 'stock-plate-purpose-uuid'
)
end

let(:tag_plate_barcode) { SBCF::SangerBarcode.new(prefix: 'DN', number: 2).machine_barcode.to_s }
let(:tag_plate_qcable_uuid) { 'tag-plate-qcable' }
let(:tag_plate_uuid) { 'tag-plate-uuid' }
let(:tag_plate_qcable) { json :tag_plate_qcable, uuid: tag_plate_qcable_uuid, lot_uuid: 'lot-uuid' }
let(:tag_template_uuid) { 'tag-layout-template-0' }
let(:transfer_template_uuid) { 'custom-pooling' }

let(:qcable_template) { create :v2_tag_layout_template, uuid: tag_template_uuid }
let(:qcable_lot) { create :v2_lot, template: qcable_template }
let(:qcable_labware) { create :v2_plate, uuid: tag_plate_uuid }
let(:qcable) { create :v2_qcable, lot: qcable_lot, labware: qcable_labware, uuid: tag_plate_qcable_uuid }

let(:tag_plate_barcode) { qcable_labware.labware_barcode.machine }
let(:tag_plate_qcable) { json :tag_plate_qcable, uuid: tag_plate_qcable_uuid }
let(:expected_transfers) { WellHelpers.stamp_hash(96) }
let(:enforce_uniqueness) { true }

Expand Down Expand Up @@ -59,7 +63,7 @@
user_uuid: user_uuid,
source_uuid: parent_plate.uuid,
destination_uuid: tag_plate_uuid,
transfer_template_uuid: transfer_template_uuid,
transfer_template_uuid: 'custom-pooling',
transfers: expected_transfers
}
}
Expand All @@ -68,7 +72,6 @@

let(:help_text) { 'This plate does not appear to be part of a larger pool. Dual indexing is optional.' }

let(:tag_lot_number) { 'tag_lot_number' }
let(:enforce_same_template_within_pool) { false }

# Setup stubs
Expand All @@ -91,14 +94,8 @@
stub_v2_barcode_printers(create_list(:v2_plate_barcode_printer, 3))
stub_v2_tag_layout_templates(templates)

# TODO: {Y24-190} Get rid of these v1 stubs after tag_layout_templates are moved to v2 in tagged_plate.rb
stub_api_get(tag_template_uuid, body: json(:tag_layout_template, uuid: tag_template_uuid))
stub_api_post(tag_template_uuid, body: json(:tag_layout_template, uuid: tag_template_uuid))

# API v1 UUID requests for a qcable via qcable_presenter.
stub_api_get(tag_plate_qcable_uuid, body: tag_plate_qcable)
stub_api_get('lot-uuid', body: json(:tag_lot, lot_number: tag_lot_number, template_uuid: tag_template_uuid))
stub_api_get('tag-lot-type-uuid', body: json(:tag_lot_type))
# API v2 requests for the qcable
stub_v2_qcable(qcable)
end

shared_examples 'it supports the plate' do
Expand All @@ -110,6 +107,12 @@

stub_v2_plate(create(:v2_plate, uuid: tag_plate_uuid, purpose_uuid: 'stock-plate-purpose-uuid'))
stub_api_v2_post('StateChange')

stub_search_and_single_result(
'Find qcable by barcode',
{ 'search' => { 'barcode' => tag_plate_barcode } },
tag_plate_qcable
)
end

scenario 'creation with the plate' do
Expand All @@ -121,31 +124,29 @@
click_on('Add an empty Tag Purpose plate')
expect(page).to have_content('Tag plate addition')
expect(find('#tag-help')).to have_content(help_text)
stub_search_and_single_result(
'Find qcable by barcode',
{ 'search' => { 'barcode' => tag_plate_barcode } },
tag_plate_qcable
)
swipe_in('Tag plate barcode', with: tag_plate_barcode)
expect(page).to have_content(tag_lot_number)
expect(page).to have_content(qcable_lot.lot_number)
expect(find('#well_A2')).to have_content(a2_tag)
click_on('Create Plate')
expect(page).to have_content('New empty labware added to the system.')
end
end

shared_examples 'it rejects the candidate plate' do
before do
stub_search_and_single_result(
'Find qcable by barcode',
{ 'search' => { 'barcode' => tag_plate_barcode } },
tag_plate_qcable
)
end

scenario 'rejects the candidate plate' do
fill_in_swipecard_and_barcode user_swipecard, plate_barcode
plate_title = find('#plate-title')
expect(plate_title).to have_text('Limber Cherrypicked')
click_on('Add an empty Tag Purpose plate')
expect(page).to have_content('Tag plate addition')
stub_search_and_single_result(
'Find qcable by barcode',
{ 'search' => { 'barcode' => tag_plate_barcode } },
tag_plate_qcable
)
swipe_in('Tag plate barcode', with: tag_plate_barcode)
expect(page).to have_button('Create Plate', disabled: true)
expect(page).to have_content(tag_error)
Expand Down
8 changes: 8 additions & 0 deletions spec/support/api_url_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,14 @@ def stub_v2_qc_file(qc_file)
allow(Sequencescape::Api::V2::QcFile).to receive(:find).with(*arguments).and_return([qc_file])
end

def stub_v2_qcable(qcable)
arguments = [{ uuid: qcable.uuid }]
query_builder = double

allow(Sequencescape::Api::V2::Qcable).to receive(:includes).and_return(query_builder)
allow(query_builder).to receive(:find).with(*arguments).and_return([qcable])
end

def stub_v2_study(study)
arguments = [{ name: study.name }]
allow(Sequencescape::Api::V2::Study).to receive(:find).with(*arguments).and_return([study])
Expand Down

0 comments on commit 87b191a

Please sign in to comment.