Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b72fe5d
Add API actions
kdp-cloud May 19, 2026
49d4a1f
Add serializers
kdp-cloud May 19, 2026
4cf5410
Add API documentation
kdp-cloud May 19, 2026
0c6343c
Add ISA Assays create API test
kdp-cloud May 19, 2026
9043bfb
Add ISA Assays API update test
kdp-cloud May 19, 2026
aacf905
Add ISA studies API test
kdp-cloud May 19, 2026
c1a4029
Add examples
kdp-cloud May 19, 2026
3946ff7
Change meta block
kdp-cloud May 19, 2026
9b0d49b
correct linked sample type
kdp-cloud May 19, 2026
91737df
Add json support for update method
kdp-cloud May 20, 2026
acf376f
Fix validation rules
kdp-cloud May 21, 2026
9ae8d70
Add input ISA tag
kdp-cloud May 21, 2026
6536694
Add rescue statement
kdp-cloud May 21, 2026
dd22e69
Simplify tests
kdp-cloud May 21, 2026
2e8fcbf
Fix double render error
kdp-cloud May 21, 2026
c2e8ca2
Add need for sample type
kdp-cloud May 21, 2026
c4a24be
Change test to include the assay stream
kdp-cloud May 21, 2026
7a17818
Fix isa_studies_controller test
kdp-cloud May 21, 2026
3994a1a
Add input ISA tag
kdp-cloud May 22, 2026
31bad4f
Copilot comments
kdp-cloud May 22, 2026
e9bcb67
Add JSON format
kdp-cloud May 22, 2026
64196b8
Add show methods
kdp-cloud May 22, 2026
0661efe
Add study ID to serializer
kdp-cloud May 22, 2026
c8cfb29
Add rescue for RecordNotFound
kdp-cloud May 22, 2026
5912b7d
Add tests
kdp-cloud May 22, 2026
1e62816
Add samples to serializers
kdp-cloud May 22, 2026
520d567
Add tests
kdp-cloud May 22, 2026
af7c910
Update post and patch responses
kdp-cloud May 22, 2026
09124a5
Copilot comments
kdp-cloud May 22, 2026
937ff5f
Update API documentation
kdp-cloud May 22, 2026
786e141
Copilot review
kdp-cloud May 26, 2026
178e4ed
Copilot review
kdp-cloud May 26, 2026
33eb42d
Add missing attributes
kdp-cloud May 26, 2026
9a15976
Add return statement
kdp-cloud May 26, 2026
262c90e
Add missing attributes
kdp-cloud May 26, 2026
e157bb4
Copilot review
kdp-cloud May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 81 additions & 29 deletions app/controllers/isa_assays_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ class ISAAssaysController < ApplicationController
include Seek::Publishing::PublishingCommon

before_action :set_up_instance_variable
before_action :find_requested_item, only: %i[edit update]
before_action :find_requested_item_for_edit, only: %i[edit update]
before_action :find_requested_item_for_show, only: :show
before_action :initialize_isa_assay, only: :create
after_action :rearrange_assay_positions_create_isa_assay, only: :create
after_action :fix_assay_linkage_for_new_assays, only: :create
Expand All @@ -12,6 +13,8 @@ class ISAAssaysController < ApplicationController
before_action :old_attributes, only: :update
after_action :update_sample_json_metadata, only: :update

api_actions :create, :update, :show

def new
study = Study.find(params[:study_id])
new_position =
Expand Down Expand Up @@ -66,7 +69,7 @@ def create
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay)
end
format.json { render json: @isa_assay, include: [params[:include]] }
format.json { render json: @isa_assay, include: [params[:include]], status: :created }
end
else
respond_to do |format|
Expand All @@ -76,6 +79,17 @@ def create
end
end

def show
respond_to do |format|
Comment thread
kdp-cloud marked this conversation as resolved.
format.json { render json: @isa_assay, include: [params[:include]] }
format.html do
flash[:notice] = "You have been redirected to the #{t('single_page')} view"
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay.id)
end
end
end

def edit
respond_to(&:html)
end
Expand All @@ -93,13 +107,18 @@ def update
end

if @isa_assay.save
flash[:notice] = "The #{t('isa_assay')} was successfully updated.<br/>".html_safe
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
respond_to do |format|
format.html do
flash[:notice] = "The #{t('isa_assay')} was successfully updated"
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay.id)
end
format.json { render json: @isa_assay, include: [params[:include]], status: :ok }
end
else
respond_to do |format|
format.html { render action: 'edit', status: :unprocessable_entity }
format.json { render json: @isa_assay.errors, status: :unprocessable_entity }
format.json { render json: json_api_errors(@isa_assay), status: :unprocessable_entity }
end
end
end
Expand Down Expand Up @@ -193,10 +212,10 @@ def sample_type_params(params)
attribute[:sample_attribute_type_id] = attribute[:sample_attribute_type][:id].to_i
elsif attribute[:sample_attribute_type][:title]
attribute[:sample_attribute_type_id] =
SampleAttributeType.where(title: attribute[:sample_attribute_type][:title]).first.id
SampleAttributeType.where(title: attribute[:sample_attribute_type][:title]).first&.id
end
end
attribute[:unit_id] = Unit.where(symbol: attribute[:unit_symbol]).first.id unless attribute[:unit_symbol].nil?
attribute[:unit_id] = Unit.where(symbol: attribute[:unit_symbol]).first&.id unless attribute[:unit_symbol].nil?
params[:sample_type][:sample_attributes_attributes] << attribute
end
end
Expand All @@ -222,7 +241,7 @@ def set_up_instance_variable
end

def old_attributes
return if @isa_assay.assay.is_assay_stream?
return if @isa_assay.assay.is_assay_stream? || @isa_assay.sample_type.nil?

@old_attributes = @isa_assay.sample_type.sample_attributes.map do |attr|
{ id: attr.id, title: attr.title }
Expand All @@ -243,34 +262,67 @@ def update_sample_json_metadata
UpdateSampleMetadataJob.perform_later(@isa_assay.sample_type, @current_user, attribute_changes)
end

def find_requested_item
def respond_with_error(status=:unprocessable_entity)
respond_to do |format|
format.html do
error_messages = @isa_assay.errors.map do |error|
helpers.content_tag(:li) do
"[" + helpers.content_tag(:b, error.attribute.to_s) + "]: #{ERB::Util.html_escape(error.message)}"
end
end
flash[:error] = helpers.content_tag(:ul) do
helpers.safe_join(error_messages)
end.html_safe
path = if @isa_assay.assay.nil?
assays_path
else
single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay)
end
redirect_to path
end

format.json { render json: json_api_errors(@isa_assay), status: status }
end
end

def find_requested_item_for_show
@isa_assay = ISAAssay.new
@isa_assay.populate(params[:id])

if @isa_assay.assay.nil?
@isa_assay.errors.add(:assay, "The #{t('isa_assay')} was not found.")
else
@isa_assay.errors.add(:assay, "You are not authorized to edit this #{t('isa_assay')}.") unless requested_item_authorized?(@isa_assay.assay)
@isa_assay.errors.add(:assay, "The #{t('isa_assay')} was not found.") if @isa_assay.assay.nil?
return respond_with_error(:not_found) if @isa_assay.errors.any?

@isa_assay.errors.add(:assay, "You are not authorized to view this #{t('isa_assay')}.") unless @isa_assay.assay.can_view?
return respond_with_error(:forbidden) if @isa_assay.errors.any?

unless @isa_assay.assay.is_assay_stream?
@isa_assay.errors.add(:sample_type, 'Sample type not found.') if @isa_assay.sample_type.nil?
return respond_with_error(:not_found) if @isa_assay.errors.any?
end
rescue ActiveRecord::RecordNotFound
render json: { errors: [{ title: 'Not Found', detail: "#{t('isa_assay')} with id '#{params[:id]}' was not found." }] },
status: :not_found
end

def find_requested_item_for_edit
@isa_assay = ISAAssay.new
@isa_assay.populate(params[:id])

@isa_assay.errors.add(:assay, "The #{t('isa_assay')} was not found.") if @isa_assay.assay.nil?
return respond_with_error(:not_found) if @isa_assay.errors.any?

@isa_assay.errors.add(:assay, "You are not authorized to edit this #{t('isa_assay')}.") unless requested_item_authorized?(@isa_assay.assay)
return respond_with_error(:forbidden) if @isa_assay.errors.any?

# Should not deal with sample type if assay has assay_class assay stream
unless @isa_assay.assay&.is_assay_stream?
if @isa_assay.sample_type.nil?
@isa_assay.errors.add(:sample_type, 'Sample type not found.')
elsif @isa_assay.sample_type.locked?
@isa_assay.errors.add(:sample_type, "The #{t('isa_assay')}'s #{t('sample_type')} is locked by a background process and cannot be edited.")
else
@isa_assay.errors.add(:sample_type, "You are not authorized to edit this #{t('isa_assay')}'s #{t('sample_type')}.") unless requested_item_authorized?(@isa_assay.sample_type)
end
end
unless @isa_assay.assay.is_assay_stream?
@isa_assay.errors.add(:sample_type, 'Sample type not found.') if @isa_assay.sample_type.nil?
return respond_with_error(:not_found) if @isa_assay.errors.any?

if @isa_assay.errors.any?
error_messages = @isa_assay.errors.map do |error|
"<li>[<b>#{error.attribute.to_s}</b>]: #{error.message}</li>"
end.join('')
flash[:error] = "<ul>#{error_messages}</ul>".html_safe
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay)
@isa_assay.errors.add(:sample_type, "The #{t('isa_assay')}'s #{t('sample_type')} is locked by a background process and cannot be edited.") if @isa_assay.sample_type.locked?
@isa_assay.errors.add(:sample_type, "You are not authorized to edit this #{t('isa_assay')}'s #{t('sample_type')}.") unless requested_item_authorized?(@isa_assay.sample_type)
return respond_with_error(:unprocessable_entity) if @isa_assay.errors.any?
end
end
end
104 changes: 84 additions & 20 deletions app/controllers/isa_studies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@ class ISAStudiesController < ApplicationController
include Seek::Publishing::PublishingCommon

before_action :set_up_instance_variable
before_action :find_requested_item, only: %i[edit update]
before_action :find_requested_item_for_edit, only: %i[edit update]
before_action :find_requested_item_for_show, only: :show
before_action :old_attributes, only: %i[update]

after_action :update_sample_json_metadata, only: :update

api_actions :create, :update, :show

def show
respond_to do |format|
Comment thread
kdp-cloud marked this conversation as resolved.
format.json { render json: @isa_study, include: [params[:include]] }
format.html do
flash[:notice] = "You have been redirected to the #{t('single_page')} view"
redirect_to single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study)
end
end
end

def new
@isa_study = ISAStudy.new({ study: { investigation_id: params[:investigation_id] } })
end
Expand All @@ -26,20 +40,20 @@ def create
@isa_study.study.sample_types = [ @isa_study.source, @isa_study.sample_collection ]

if @isa_study.save
flash[:notice] = "The #{t('isa_study')} was succesfully created.<br/>".html_safe
flash[:notice] = "The #{t('isa_study')} was successfully created"

respond_to do |format|
format.html do
redirect_to single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study)
end
format.json { render json: @isa_study, include: [params[:include]] }
format.json { render json: @isa_study, include: [params[:include]], status: :created }
end

else
respond_to do |format|
format.html { render action: 'new', status: :unprocessable_entity }
format.json { render json: @isa_study.errors, status: :unprocessable_entity }
format.json { render json: json_api_errors(@isa_study), status: :unprocessable_entity }
end
Comment thread
kdp-cloud marked this conversation as resolved.
end
end
Expand Down Expand Up @@ -69,12 +83,20 @@ def update
end

if @isa_study.save
redirect_to single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study.id)
respond_to do |format|
format.html do
flash[:notice] = "The #{t('isa_study')} was successfully updated"
redirect_to single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study.id)

end

format.json { render json: @isa_study, include: [params[:include]], status: :ok }
end
else
respond_to do |format|
format.html { render action: 'edit', status: :unprocessable_entity }
format.json { render json: @isa_study.errors, status: :unprocessable_entity }
format.json { render json: json_api_errors(@isa_study), status: :unprocessable_entity }
end
end
end
Expand Down Expand Up @@ -172,27 +194,69 @@ def set_up_instance_variable
@single_page = true
end

def find_requested_item
def respond_with_error(status=:unprocessable_entity)
respond_to do |format|
format.html do
error_messages = @isa_study.errors.map do |error|
helpers.content_tag(:li) do
"[" + helpers.content_tag(:b, error.attribute.to_s) + "]: #{ERB::Util.html_escape(error.message)}"
end
end
flash[:error] = helpers.content_tag(:ul) do
helpers.safe_join(error_messages)
end.html_safe
path = if @isa_study.study.nil?
studies_path
else
single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study)
end
redirect_to path
end
Comment thread
kdp-cloud marked this conversation as resolved.

format.json { render json: json_api_errors(@isa_study), status: status }
end
end

def find_requested_item_for_show
@isa_study = ISAStudy.new
@isa_study.populate(params[:id])

# Check whether base objects exist
@isa_study.errors.add(:study, "The #{t('isa_study')} was not found.") if @isa_study.study.nil?
@isa_study.errors.add(:study, "You are not authorized to edit this #{t('isa_study')}.") unless requested_item_authorized?(@isa_study.study)
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} source' #{t('sample_type')} not found.") if @isa_study.source.nil?
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} sample' #{t('sample_type')} not found.") if @isa_study.sample_collection.nil?
return respond_with_error(:not_found) if @isa_study.errors.any?

# Check authorizations
@isa_study.errors.add(:study, "You are not authorized to view this #{t('isa_study')}.") unless @isa_study.study.can_view?
return respond_with_error(:forbidden) if @isa_study.errors.any?

rescue ActiveRecord::RecordNotFound
render json: { errors: [{ title: 'Not Found', detail: "#{t('isa_study')} with id '#{params[:id]}' was not found." }] },
status: :not_found
return false
end

def find_requested_item_for_edit
@isa_study = ISAStudy.new
@isa_study.populate(params[:id])

# Check whether base objects exist
@isa_study.errors.add(:study, "The #{t('isa_study')} was not found.") if @isa_study.study.nil?
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} source' #{t('sample_type')} not found.") if @isa_study.source.nil?
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} source' #{t('sample_type')} is locked by a background process.") if @isa_study.source.locked?
@isa_study.errors.add(:sample_type, "You are not authorized to edit the '#{t('isa_study')} source' #{t('sample_type')}.") unless requested_item_authorized?(@isa_study.source)
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} sample' #{t('sample_type')} not found.") if @isa_study.sample_collection.nil?
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} sample' #{t('sample_type')} is locked by a background process.") if @isa_study.sample_collection.locked?
return respond_with_error(:not_found) if @isa_study.errors.any?

# Check authorizations
@isa_study.errors.add(:study, "You are not authorized to edit this #{t('isa_study')}.") unless requested_item_authorized?(@isa_study.study)
@isa_study.errors.add(:sample_type, "You are not authorized to edit the '#{t('isa_study')} source' #{t('sample_type')}.") unless requested_item_authorized?(@isa_study.source)
@isa_study.errors.add(:sample_type, "You are not authorized to edit the '#{t('isa_study')} sample collection' #{t('sample_type')}.") unless requested_item_authorized?(@isa_study.sample_collection)
return respond_with_error(:forbidden) if @isa_study.errors.any?

if @isa_study.errors.any?
error_messages = @isa_study.errors.map do |error|
"<li>[<b>#{error.attribute.to_s}</b>]: #{error.message}</li>"
end.join('')
flash[:error] = "<ul>#{error_messages}</ul>".html_safe
redirect_to single_page_path(id: @isa_study.study.projects.first, item_type: 'study',
item_id: @isa_study.study)
end
# Check whether sample types are locked
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} source' #{t('sample_type')} is locked by a background process.") if @isa_study.source&.locked?
@isa_study.errors.add(:sample_type, "'#{t('isa_study')} sample' #{t('sample_type')} is locked by a background process.") if @isa_study.sample_collection&.locked?
return respond_with_error(:unprocessable_entity) if @isa_study.errors.any?
end
end
Loading