Add endpoint for assigning multiple task to inventory [SCI-10429]

This commit is contained in:
wandji20 2024-03-27 15:26:25 +01:00
parent 11c78d3074
commit abbd37b9c0
6 changed files with 163 additions and 29 deletions

View file

@ -3,12 +3,13 @@
class MyModuleRepositoriesController < ApplicationController
include ApplicationHelper
before_action :load_my_module
before_action :load_my_module, except: :assign_my_modules
before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html create)
before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption)
before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption assign_my_modules)
before_action :check_repository_view_permissions, except: %i(repositories_dropdown_list repositories_list_html create)
before_action :check_repository_row_consumption_permissions, only: %i(consume_modal update_consumption)
before_action :check_assign_repository_records_permissions, only: %i(update create)
before_action :load_my_modules, only: :assign_my_modules
def index_dt
@draw = params[:draw].to_i
@ -41,6 +42,31 @@ class MyModuleRepositoriesController < ApplicationController
render rows_view
end
def assign_my_modules
assigned_count = 0
skipped_count = 0
status = :ok
ActiveRecord::Base.transaction do
@my_modules.find_each do |my_module|
service = RepositoryRows::MyModuleAssignUnassignService.call(
my_module:,
repository: @repository,
user: current_user,
params:
)
unless service.succeed?
status = :unprocessable_entity
raise ActiveRecord::Rollback
end
assigned_count += service.assigned_rows_count
skipped_count += (params[:rows_to_assign].length - service.assigned_rows_count)
end
end
render json: { assigned_count:, skipped_count: }, status:
end
def create
repository_row = RepositoryRow.find(params[:repository_row_id])
repository = repository_row.repository
@ -215,6 +241,12 @@ class MyModuleRepositoriesController < ApplicationController
render_404 unless @repository
end
def load_my_modules
@my_modules = MyModule.where(id: params[:my_module_ids])
render_403 unless @my_modules.all? { |my_module| can_assign_my_module_repository_rows?(my_module) }
end
def check_my_module_view_permissions
render_403 unless can_read_my_module?(@my_module)
end

View file

@ -94,7 +94,7 @@
</label>
<SelectDropdown
:value="selectedTask"
:value="selectedTasks"
:disabled="!selectedExperiment"
:searchable="true"
ref="tasksSelector"
@ -102,6 +102,8 @@
:options="tasks"
:isLoading="tasksLoading"
:placeholder="tasksSelectorPlaceholder"
:multiple="true"
:withCheckboxes="true"
:no-options-placeholder="
i18n.t(
'repositories.modal_assign_items_to_task.body.task_select.no_options_placeholder'
@ -115,7 +117,7 @@
type="button"
class="btn btn-primary"
data-dismiss="modal"
:disabled="!selectedTask"
:disabled="!selectedTasks.length"
@click="assign"
>
{{ i18n.t("repositories.modal_assign_items_to_task.assign.text") }}
@ -127,6 +129,7 @@
</template>
<script>
/* global HelperModule */
import SelectDropdown from "../shared/select_dropdown.vue";
export default {
@ -143,7 +146,7 @@ export default {
tasks: [],
selectedProject: null,
selectedExperiment: null,
selectedTask: null,
selectedTasks: [],
projectsLoading: null,
experimentsLoading: null,
tasksLoading: null
@ -206,9 +209,6 @@ export default {
taskURL() {
return `${this.urls.tasks}?experiment_id=${this.selectedExperiment
|| ''}`;
},
assignURL() {
return this.urls.assign.replace(':module_id', this.selectedTask);
}
},
watch: {
@ -259,7 +259,7 @@ export default {
});
},
changeTask(value) {
this.selectedTask = value;
this.selectedTasks = value;
},
resetProjectSelector() {
this.projects = [];
@ -271,7 +271,7 @@ export default {
},
resetTaskSelector() {
this.tasks = [];
this.selectedTask = null;
this.selectedTasks = [];
},
resetSelectors() {
this.resetTaskSelector();
@ -279,20 +279,33 @@ export default {
this.resetProjectSelector();
},
assign() {
if (!this.selectedTask) return;
if (!this.selectedTasks.length) return;
$.ajax({
url: this.assignURL,
type: 'PATCH',
url: this.urls.assign,
type: 'POST',
dataType: 'json',
data: { rows_to_assign: this.rowsToAssign }
}).done(({ assigned_count }) => {
const skipped_count = this.rowsToAssign.length - assigned_count;
if (skipped_count) {
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_some_assignments_success', { assigned_count, skipped_count }), 'success');
data: {
rows_to_assign: this.rowsToAssign,
my_module_ids: this.selectedTasks
}
}).done(({ assigned_count: assignedCount, skipped_count: skippedCount }) => {
if (skippedCount) {
HelperModule.flashAlertMsg(
this.i18n.t(
'repositories.modal_assign_items_to_task.assign.flash_some_assignments_success',
{ assigned_count: assignedCount, skipped_count: skippedCount }
),
'success'
);
} else {
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_all_assignments_success', { count: assigned_count }), 'success');
HelperModule.flashAlertMsg(
this.i18n.t(
'repositories.modal_assign_items_to_task.assign.flash_all_assignments_success',
{ count: assignedCount }
),
'success'
);
}
}).fail(() => {
HelperModule.flashAlertMsg(this.i18n.t('repositories.modal_assign_items_to_task.assign.flash_assignments_failure'), 'danger');
@ -309,7 +322,7 @@ export default {
.dataTable()
.api()
.ajax
.reload();
.reload(null, false);
}
}
};

View file

@ -1,6 +1,6 @@
<div
class="assign-items-to-task-modal-container"
data-assign-url="<%= my_module_repository_path(":module_id") %>"
data-assign-url="<%= assign_my_modules_url(id: @repository.id) %>"
data-projects-url="<%= inventory_assigning_project_filter_projects_path %>"
data-experiments-url="<%= inventory_assigning_experiment_filter_experiments_path %>"
data-tasks-url="<%= inventory_assigning_my_module_filter_my_modules_path %>"

View file

@ -2226,26 +2226,26 @@ en:
modal_assign_items_to_task:
title: "Assign to task"
body:
description: "Type in the fields below to find the right task."
description: "Select the fields below to assign item to task(s)."
project_select:
label: "Project"
placeholder: "Enter project name"
placeholder: "Select or enter Project name"
no_options_placeholder: "No projects available to select"
experiment_select:
label: "Experiment"
placeholder: "Enter Experiment name"
placeholder: "Select or enter Experiment name"
disabled_placeholder: "Select Project to enable Experiment"
no_options_placeholder: "No experiments available to select"
task_select:
label: "Task"
placeholder: "Enter Task name"
disabled_placeholder: "Select Experiment to enable Task"
disabled_placeholder: "Select or enter Task name"
no_options_placeholder: "No tasks available to assign items"
assign:
text: "Assign to task"
flash_all_assignments_success: "Successfully assigned %{count} item(s) to the task."
flash_some_assignments_success: "Successfully assigned %{assigned_count} item(s) to the task. %{skipped_count} item(s) were already assigned to the task."
flash_assignments_failure: "Failed to assign item(s) to task."
flash_all_assignments_success: "Successfully assigned %{count} item(s) to the task(s)."
flash_some_assignments_success: "Successfully assigned %{assigned_count} item(s) to the task(s). %{skipped_count} item(s) were already assigned to the task(s)."
flash_assignments_failure: "Failed to assign item(s) to task(s)."
modal_delete_record:
title: "Delete items"
notice: "Are you sure you want to delete the selected item(s)?"

View file

@ -553,6 +553,7 @@ Rails.application.routes.draw do
end
end
end
post 'repository/:id/assign_my_modules', to: 'my_module_repositories#assign_my_modules', as: :assign_my_modules
resources :steps, only: %i(index update destroy show) do
resources :step_orderable_elements do

View file

@ -0,0 +1,88 @@
# frozen_string_literal: true
require 'rails_helper'
describe RepositoryRows::MyModuleAssignUnassignService do
let(:user) { create :user }
let(:team) { create :team, created_by: user }
let(:project) { create(:project, team:, created_by: user) }
let(:experiment) { create(:experiment, :with_tasks, project:) }
let(:my_module) { create(:my_module, experiment:) }
let(:repository) { create :repository, team:, created_by: user }
let(:rows) do
create_list(:repository_row, 10, repository:) do |row, i|
row.name = "My Row (#{i + 1})"
end
end
let(:service_call) { described_class.call(**valid_attributes) }
let(:valid_attributes) do
{
my_module:,
repository:,
user:,
params:
}
end
let(:rows_to_assign) { rows.take(6).pluck(:id) }
let(:rows_to_unassign) { repository.repository_rows.where.not(id: rows_to_assign).order(:id).pluck(:id) }
context 'single module' do
context 'assigning items' do
let(:rows_to_assign) { rows.take(6).pluck(:id) }
let(:rows_to_unassign) { repository.repository_rows.where.not(id: rows_to_assign).order(:id).pluck(:id) }
let(:params) do
{
rows_to_assign:,
rows_to_unassign:,
downstream: false
}
end
it 'creates a new assignment' do
expect { service_call }.to change { MyModuleRepositoryRow.count }.by rows_to_assign.count
end
it 'unassigns and assigns items' do
rows_to_unassign.each do |row_id|
my_module.my_module_repository_rows.create!(repository_row_id: row_id, assigned_by: user)
end
service_call
expect(
my_module.my_module_repository_rows.order(:repository_row_id).pluck(:repository_row_id)
).to eq rows_to_assign
end
it 'does nothing' do
rows_to_assign.each do |row_id|
my_module.my_module_repository_rows.create!(repository_row_id: row_id, assigned_by: user)
end
expect { service_call }.to change { MyModuleRepositoryRow.count }.by 0
end
end
context 'unassigning' do
let(:params) do
{
rows_to_assign: [],
rows_to_unassign: rows.pluck(:id),
downstream: false
}
end
let(:rows_to_assign) { [] }
let(:rows_to_unassign) { rows.pluck(:id) }
it 'unassigns items' do
rows_to_unassign.each do |row_id|
my_module.my_module_repository_rows.create!(repository_row_id: row_id, assigned_by: user)
end
initial_count = my_module.my_module_repository_rows.count
service_call
expect(my_module.my_module_repository_rows.count).to eq 0
expect(my_module.my_module_repository_rows.count).to_not eq initial_count
end
end
end
end